I have a Recyclerview that is managed by an adapter. For each item in the recycler I inflate a complex view that contains also a horizontal progressbar which takes the whole width of screen.
I have to position a baloon TextView with the percentage value (20% , 60% etc) that points to the progressbar like an indicator to the amount of progress. I tried using this code
int[] startPosition = new int[2];
Integer progresswidth;
WindowManager wm = (WindowManager) contextthis.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
progresswidth = size.x;
holder.horiz_progress.getLocationOnScreen(startPosition);
float exactvalue = (progresswidth * currentitem.getMatchPercent()) / 100;
startPosition[0] = startPosition[0] + Math.round(exactvalue);
startPosition[0] = startPosition[0] - holder.baloon_txt.getWidth() / 3 ;
startPosition[1] = startPosition[1] + 10;
holder.baloon_txt.setX(startPosition[0]);
holder.baloon_txt.setY(startPosition[1]);
But the problem is that holder.horiz_progress.getLocationOnScreen always returns 0 so I cannot position the balloon_txt.
I had a similar issue inside an activity and there i resolved it overriding OnWindowFocusChanged but this is inside the adapter so I don't know how to get it done.
EDIT
My current adapter code:
public class ResultsAdapter extends RecyclerView.Adapter<ResultsAdapter.ViewHolder>{
private List<ResultsItem> mResults;
private View mSelectedView;
private int mSelectedPosition;
Context contextthis;
android.os.Handler handler;
int[] startPosition = new int[2];
public ResultsAdapter(Context context,List<ResultsItem> resultsItemList) {
this.mResults = resultsItemList;
contextthis = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.results_item, null);
final ViewHolder viewHolder = new ViewHolder(v);
viewHolder.results_likeimg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
return viewHolder;
}
#Override
public void onBindViewHolder( final ViewHolder holder, int position) {
final ResultsItem currentitem = mResults.get(position);
//set Image
if(currentitem.getImageslist().get(0).getPicture() != null)
ImageLoader.getInstance().displayImage(currentitem.getImageslist().get(0).getPicture(), holder.results_img);
//baloon set
holder.baloon_txt.setText(currentitem.getMatchPercent() + "% " + "Match");
holder.horiz_progress.setProgress(currentitem.getMatchPercent());
final View view = holder.horiz_progress;
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getLocationOnScreen(startPosition);
Integer progresswidth;
WindowManager wm = (WindowManager) contextthis.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
progresswidth = size.x;
float exactvalue = (progresswidth * currentitem.getMatchPercent()) / 100;
startPosition[0] = startPosition[0] + Math.round(exactvalue);
startPosition[0] = startPosition[0] - holder.baloon_txt.getWidth() / 3 ;
startPosition[1] = startPosition[1] + 10;
holder.baloon_txt.setX(startPosition[0]);
holder.baloon_txt.setY(startPosition[1]);
}
});
//logo
if(currentitem.getPriceslist().get(0).getSource() != null)
ImageLoader.getInstance().displayImage(currentitem.getPriceslist().get(0).getSource(), holder.results_logo_img);
//description
holder.description_txt.setText(currentitem.getDescription());
//price
holder.price_curr.setText(currentitem.getPriceslist().get(0).getCurrency());
holder.price_txt.setText(String.valueOf(currentitem.getPriceslist().get(0).getPrice()));
}
#Override
public int getItemCount() {
return mResults.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView results_img, results_dislikeimg, results_likeimg, results_logo_img;
ProgressBar horiz_progress;
TextView baloon_txt, price_txt, description_txt, buybtn, sharebtn, price_curr;
public ViewHolder(View view) {
super(view);
this.results_img = (ImageView) view.findViewById(R.id.resultsitem_img);
this.results_dislikeimg = (ImageView) view.findViewById(R.id.results_item_dislike);
this.results_likeimg = (ImageView) view.findViewById(R.id.resultsitem_like);
this.results_logo_img = (ImageView) view.findViewById(R.id.logoimg);
this.horiz_progress = (ProgressBar) view.findViewById(R.id.progressBar_horizontal);
this.baloon_txt = (TextView) view.findViewById(R.id.baloonMatch_txt);
this.price_txt = (TextView) view.findViewById(R.id.price_txt);
this.description_txt = (TextView) view.findViewById(R.id.description_txt);
this.buybtn = (TextView) view.findViewById(R.id.buybtn);
this.sharebtn = (TextView) view.findViewById(R.id.sharebtn);
this.price_curr = (TextView) view.findViewById(R.id.price_curr);
}
}
}
getLocation() returns 0 because the view has not been laid out yet. You need to set a layout listener:
final View view = holder.horiz_progress;
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Do what you need to do here.
// Then remove the listener:
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
Related
When expanding and collapsing my Recyclerview items, divider lines drawn multiple times or overdrawn with items.
Also when expanding and collapsing views, dividers thickness is getting reduced.
My problem is divider line drawn each and every time I expand and collapse item in Recyclerview.
So is it possible to prevent divider line drawing if it already drawn?
While expanding an item, divider line will move according to the view.?
Below is my RecyclerView Decoration Class used for divider line,
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
/**
* Create a decoration that draws a line in the given color and width between the items in the view.
* #param context a context to access the resources.
* #param color the color of the separator to draw.
* #param heightDp the height of the separator in dp.
*/
public SeparatorDecoration(#NonNull Context context, #ColorInt int color,
#FloatRange(from = 0, fromInclusive = false) float heightDp) {
mPaint = new Paint();
mPaint.setColor(color);
final float thickness = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
heightDp, context.getResources().getDisplayMetrics());
mPaint.setStrokeWidth(thickness);
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we want to retrieve the position in the list
final int position = params.getViewAdapterPosition();
// and add a separator to any view but the last one
if (position <state.getItemCount()) {
outRect.set(40, 0, 40, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
#Override
public void onDrawOver(#NonNull Canvas c, #NonNull RecyclerView parent, #NonNull RecyclerView.State state) {
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
// get the view
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < parent.getChildCount()) {
final int ty = (int)(view.getTranslationY() + 0.5f);
final int top = view.getBottom() - params.bottomMargin + ty;
final int bottom = top + (int) mPaint.getStrokeWidth();
c.drawLine(view.getLeft(), view.getBottom() + offset, view.getRight(), view.getBottom() + offset, mPaint);
}
}
}
}
below is my RecyclerView Adapter class,
public class DisplayNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Context context;
private List<NotificationDetails> notificationRecords;
private DeleteNotificationListener deleteNotificationListener;
private String TAG = DisplayNotificationAdapter.class.getSimpleName();
interface DeleteNotificationListener {
void updateNotificationList(List<NotificationDetails> details);
}
public DisplayNotificationAdapter(Context context, DeleteNotificationListener listener, List < NotificationDetails > notificationRecordsList) {
this.context = context;
this.deleteNotificationListener = listener;
this.notificationRecords = notificationRecordsList;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder (#NonNull ViewGroup parent, int viewType){
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
AndroidLogger.log(5,TAG,"oncreate");
View listItem = layoutInflater.inflate(R.layout.display_notification_recycler_view_list_item, parent, false);
return new NotificationViewHolder(listItem);
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder (#NonNull RecyclerView.ViewHolder holder,int position){
NotificationDetails notification = notificationRecords.get(position);
NotificationViewHolder viewHolder = (NotificationViewHolder) holder;
String currentDateString = DateFormat.getDateInstance().format(Long.parseLong(notification.getTimeStamp()));
String filePath=generateFilePath(notification.getFileName());
Bitmap myBitmap = BitmapFactory.decodeFile(filePath);
#SuppressLint("SimpleDateFormat")
DateFormat dateFormat = new SimpleDateFormat("hh:mm aa");
String time = dateFormat.format(Long.parseLong(notification.getTimeStamp()));
if (notification.isExpanded()) {
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
expandView(viewHolder.notificationImageview);
}
else {
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
collapseView(viewHolder.notificationImageview);
}
viewHolder.notificationImageview.setImageBitmap(myBitmap);
viewHolder.notificationTextView.setText(notification.getMessage());
viewHolder.notificationTimeTextView.setText(time);
Calendar now = Calendar.getInstance();
Calendar date = Calendar.getInstance();
date.setTimeInMillis(Long.parseLong(notification.getTimeStamp()));
viewHolder.expandCollapseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(notification.isExpanded()) {
notification.setExpanded(false);
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
viewHolder.notificationTextView.setMaxLines(1);
notifyItemChanged(position);
}
else {
notification.setExpanded(true);
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
viewHolder.notificationTextView.setMaxLines(Integer.MAX_VALUE);
notifyItemChanged(position);
}
}
});
if (now.get(Calendar.DATE) == date.get(Calendar.DATE))
viewHolder.notificationDateTextView.setText("Today");
else if (now.get(Calendar.DATE) - date.get(Calendar.DATE) == 1)
viewHolder.notificationDateTextView.setText("Yesterday");
else
viewHolder.notificationDateTextView.setText(currentDateString);
if(notification.getTitle()==null)
viewHolder.notificationTitleTextView.setText("title");
else
viewHolder.notificationTitleTextView.setText(notification.getTitle());
}
private String generateFilePath(String fileName) {
File imageFileDirectory = context.getDir("image", Context.MODE_PRIVATE); //Creating an internal dir;
if (!imageFileDirectory.exists()) {
imageFileDirectory.mkdirs();
}
/*
* app server provide "U" file name after we set read status they provide same file name as "R"
*/
String createFilePath = imageFileDirectory + "/" + fileName;
return createFilePath;
}
public void removeSingleNotification ( int position){
DatabaseHelper databaseHelper = new DatabaseHelper(context);
databaseHelper.deleteSingleNotificationRecord(notificationRecords.get(position).getId());
notificationRecords.remove(position);
deleteNotificationListener.updateNotificationList(notificationRecords);
notifyDataSetChanged();
}
private void removeFromList (String id) {
for (NotificationDetails detail : notificationRecords) {
if (detail.getId().equalsIgnoreCase(id))
notificationRecords.remove(detail);
}
}
#Override
public int getItemCount () {
return notificationRecords.size();
}
public void expandView(final View v) {
int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
final int targetHeight = v.getMeasuredHeight();
// Older versions of android (pre API 21) cancel animations for views with a height of 0.
v.getLayoutParams().height = 1;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? ViewGroup.LayoutParams.WRAP_CONTENT
: (int)(targetHeight * interpolatedTime);
v.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// Expansion speed of 1dp/ms
a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public void collapseView(final View v) {
//collapse(pos);
final int initialHeight = v.getMeasuredHeight();
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 1){
v.setVisibility(View.GONE);
}else{
v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
v.requestLayout();
}
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// Collapse speed of 1dp/ms
a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public static class NotificationViewHolder extends RecyclerView.ViewHolder {
private TextView notificationTextView, notificationDateTextView, notificationTimeTextView, notificationTitleTextView;
private ImageView notificationImageview,expandCollapseImageView;
private ConstraintLayout parent;
public NotificationViewHolder(#NonNull View itemView) {
super(itemView);
notificationTextView = itemView.findViewById(R.id.notification_text_view);
notificationDateTextView = itemView.findViewById(R.id.notification_date_text_view);
notificationTimeTextView = itemView.findViewById(R.id.notification_time_text_view);
notificationTitleTextView = itemView.findViewById(R.id.notification_title_text_view);
notificationImageview = itemView.findViewById(R.id.notification_image_view);
expandCollapseImageView = itemView.findViewById(R.id.expand_collapse_arrow);
parent = itemView.findViewById(R.id.notification_parent);
}
}
}
UPDATE
I doesn't able to solve this issue. So instead of using RecyclerView.ItemDecoration I have used a View inside layout like below,
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="#+id/view_div"
android:background="#color/grey"
Doing like above solves the issue.
[This is my screen contains the issue][1]][1]
https://i.stack.imgur.com/CBY80.jpg
I created a recyclerview with a list of 'events'.This works fine for a list of events that is below 5. but as soon as i get 6 or more events in the list the last event will not expand when clicked, instead it dissapears. the closing animation also stops working with more than 6 events in the list.
how it should behave:
User taps event > view expands to full screen
User taps an expanded event > view collapses back to it's original size
User taps an event while another event is expanded > expanded event is set to original height en tapped event expands to fullscreen
current behavior:
User taps event > all views expand correctly except for the last item in the list
User taps expanded event > view collapses but does not animate
User taps an event while another event is expanded > expanded event collapses and tapped event expands correctly
User taps the last event in the list > the event dissapears (probably decreased it's size to below 0)
I know it probably has something to do with the way the recyclerview reuses its views when they are out of the screen. To fix this i check the position of the tapped event by the eventId instead of the position in the list, but this still leaves the issues that i talked about above.
public class EventRecyclerAdapter extends RecyclerView.Adapter<EventRecyclerAdapter.ViewHolder> {
private Context c;
private List<Event> items = new ArrayList<>();
private RelativeLayout container;
private int screenheight;
private EventListFragment eventListFragment;
private int expandedPosition = -1;
private static final String TAG = "EventRecyclerAdapter";
public interface ItemClickedListener {
void itemClicked(int position);
}
private ItemClickedListener itemClickedListener;
public EventRecyclerAdapter(List<Event> itemlist, Context c, EventListFragment eventListFragment, ItemClickedListener listener) {
this.items = itemlist;
this.c = c;
this.eventListFragment = eventListFragment;
this.itemClickedListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, null);
WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenheight = size.y;
// Get the screen height from the device
Resources r = c.getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, r.getDisplayMetrics());
screenheight -= px;
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
Event event = items.get(position);
// - get data from your itemsData at this position
// - replace the contents of the view with that itemsData
viewHolder.tvName.setText(event.getName());
viewHolder.tvLocation.setText(event.getLocation().getName());
viewHolder.tvDate.setText(Helper.dateDoubleToString(event.getStartDate()));
viewHolder.tvTicketCount.setText(String.valueOf(event.getNumberOfTickets()));
viewHolder.background.setBackgroundColor(Color.GRAY);
viewHolder.eventId = event.getId();
// Load the background image
if (event.getEventImageId() != null) {
Picasso.with(c).load(Helper.imageUrlString(event.getEventImageId())).into(viewHolder.background);
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
viewHolder.background.setColorFilter(filter);
}
// Check if the view needs to be expanded, collapsed or just drawn normally.
if (expandedPosition == event.getId()) {
if (event.expanded) {
collapseView(viewHolder, event);
} else if (!event.expanded) {
expandView(viewHolder, position, event);
}
} else {
setContainerHeight(viewHolder, event);
}
}
private void expandView(final EventRecyclerAdapter.ViewHolder viewHolder, final int pos, Event event) {
ResizeAnimation resizeAnimation = new ResizeAnimation(
viewHolder.container,
viewHolder.container.getHeight(),
screenheight
);
resizeAnimation.setDuration(Constants.ANIMATION_SPEED);
resizeAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
viewHolder.infoContainer.setVisibility(View.VISIBLE);
viewHolder.closeIcon.setVisibility(View.VISIBLE);
itemClickedListener.itemClicked(pos);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
viewHolder.itemView.startAnimation(resizeAnimation);
viewHolder.expanded = true;
event.expanded = true;
}
private void collapseView(final EventRecyclerAdapter.ViewHolder viewHolder, Event event) {
ResizeAnimation resizeAnimation = new ResizeAnimation(
viewHolder.container,
viewHolder.container.getHeight(),
getContainerCollapsedHeight()
);
resizeAnimation.setDuration(Constants.ANIMATION_SPEED);
viewHolder.infoContainer.setVisibility(View.INVISIBLE);
viewHolder.closeIcon.setVisibility(View.INVISIBLE);
viewHolder.itemView.startAnimation(resizeAnimation);
viewHolder.expanded = false;
event.expanded = false;
}
private void setContainerHeight(EventRecyclerAdapter.ViewHolder viewHolder, Event event) {
viewHolder.container.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getContainerCollapsedHeight()));
viewHolder.infoContainer.setVisibility(View.INVISIBLE);
viewHolder.closeIcon.setVisibility(View.INVISIBLE);
event.expanded = false;
viewHolder.expanded = false;
}
private int getContainerCollapsedHeight() {
int containerHeight;
// Define the item containers height
if (items.size() <= 3) {
containerHeight = screenheight / items.size();
} else {
containerHeight = screenheight / 3;
}
return containerHeight;
}
/**
* Clear all current data and swap add the new data list.
* The expanded position also gets reset
* #param events
*/
public void swap(List<Event> events) {
this.items.clear();
this.items.addAll(events);
this.expandedPosition = -1;
Log.v(TAG,"SWAP SIZE : " + items.size());
notifyDataSetChanged();
}
// inner class to hold a reference to each item of RecyclerView
class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvLocation, tvDate, tvTicketCount;
public TextView tvName;
public ImageView background;
public View container;
public View infoContainer;
public TextView closeIcon;
public int eventId;
public boolean expanded = false;
public ViewHolder(final View itemLayoutView) {
super(itemLayoutView);
tvName = (TextView) itemLayoutView.findViewById(R.id.tvName);
tvLocation = (TextView) itemLayoutView.findViewById(R.id.tvLocation);
tvDate = (TextView) itemLayoutView.findViewById(R.id.tvDate);
background = (ImageView) itemLayoutView.findViewById(R.id.background);
tvTicketCount = (TextView) itemLayoutView.findViewById(R.id.ticket_count);
container = itemLayoutView.findViewById(R.id.list_item_container);
infoContainer = itemLayoutView.findViewById(R.id.info_container);
closeIcon = (TextView) itemLayoutView.findViewById(R.id.close_icon);
infoContainer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Activity mainActivity = (Activity) c;
FragmentManager fm = mainActivity.getFragmentManager();
//add
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.slide_to_top, R.animator.slide_from_bottom);
ft.addToBackStack(ft.toString());
ft.add(R.id.content_frame, EventFragment.newInstance(items.get(getAdapterPosition())), Constants.EVENT_FRAGMENT_TAG);
//commit change
ft.commit();
}
});
container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
expandedPosition = eventId;
notifyDataSetChanged();
}
});
}
}
// Return the size of your itemsData (invoked by the layout manager)
#Override
public int getItemCount() {
return items.size();
}
}
i think it's somehow running the collapseView method when i tap the last item in the list, causing its height to become below 0. But i'm unable to figure out why this is happening.
I hope someone is able to spot what's wrong here.
you can try this in OnClilck of recycleview Item
#Override
public void onClick(View view)
{
LayoutParams params = view.getLayoutParams();
if (!large)
{
params.height = 2 * view.getHeight();
} else {
params.height = view.getHeight()/2;
}
large = !large;
view.setLayoutParams(params);
}
I wants google ads in my listview at specific positions in portrait mode.
it causes error when clicking at ads and when back to app.
and sometimes when no error occur the ads view height increase and ads are floating when scroll listview.
public class ListAdaptor extends BaseAdapter {
private Context context;
AdView adView = null;
private ArrayList<String> titleList, descriptionList, pubDateList,
thumbnailList;
// int count = 2;
// private Context context;
private int listCount = 15;
//private final float mDensity;
int imageSize = 50;
LayoutInflater inflater;
int pageNumber = 0;
public ListAdaptor(Context c, ArrayList<String> titleList,
ArrayList<String> descriptionList, ArrayList<String> pubDateList,
ArrayList<String> thumbnailList, int pageNumber) {
context = c;
this.titleList = titleList;
this.descriptionList = descriptionList;
this.pubDateList = pubDateList;
this.thumbnailList = thumbnailList;
this.pageNumber = pageNumber;
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
// Log.d("imageSize "+display.getWidth(),"=="+imageSize);
imageSize = size.x / 3;
// Log.d("imageSize "+display.getHeight(),"="+imageSize);
//mDensity = c.getResources().getDisplayMetrics().density;
inflater = LayoutInflater.from(c);
// for (int i = 2; i < titleList.size(); i = i + 6) {
// titleList.add(i, "null");
// descriptionList.add(i, "");
// thumbnailList.add(i, "");
// pubDateList.add(i, "");
// i++;
// }
listCount = titleList.size();
}
#Override
public int getCount() {
return listCount;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#SuppressWarnings("deprecation")
#SuppressLint("InflateParams")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (titleList.get(position).equalsIgnoreCase("null")) {
if (convertView == null || !(convertView instanceof AdView))
{
// Create a new AdView
if(adView == null)
{
try{
// Create a banner ad. The ad size and ad unit ID must be set before calling loadAd.
adView = new AdView(context.getApplicationContext());
adView.setAdSize(com.google.android.gms.ads.AdSize.BANNER);
adView.setAdUnitId(context.getString(R.string.banner_ad_unit_id));
// Convert the default layout parameters so that they play nice
// with
// ListView.
float density = context.getResources().getDisplayMetrics().density;
int height = Math.round(AdSize.BANNER.getHeight() * density);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(
AbsListView.LayoutParams.FILL_PARENT, height);
adView.setLayoutParams(params);
AdRequest adRequest = new AdRequest.Builder().
addTestDevice(Secure.getString(context.getContentResolver()
,Secure.ANDROID_ID)).build();
adView.loadAd(adRequest);
adView.setAdListener(new AdListener() {
#Override
public void onAdOpened() {
// Save app state before going to the ad overlay.
}
});
}catch(Exception e){e.printStackTrace();}
}
return adView;
} else {
// Don’t instantiate new AdView, reuse old one
return convertView;
}
} else {
if (convertView == null || convertView instanceof AdView) {
view = inflater.inflate(R.layout.card_item, null);
// view.setLayoutParams(new
// ListView.LayoutParams(LayoutParams.MATCH_PARENT,(int) (90 *
// mDensity + 0.5f)));
} else {
view = convertView;
}
ImageView imgCardLitImage = (ImageView) view
.findViewById(R.id.imgCardLitImage);
TextView tvCardLitTitle = (TextView) view
.findViewById(R.id.tvCardLitTitle);
TextView tvCardListPubDate = (TextView) view
.findViewById(R.id.tvPubDate);
TextView tvCardLitDescription = (TextView) view
.findViewById(R.id.tvDescription);
ImageView imageVideoOverLay = (ImageView) view
.findViewById(R.id.imageVideoOverLay);
if (pageNumber == 1)
imageVideoOverLay.setVisibility(View.VISIBLE);
else
imageVideoOverLay.setVisibility(View.GONE);
/*
* If ImageList item is not available ,then display Description with
* Title
*/
if (thumbnailList.get(position) == null
&& descriptionList.get(position) != null) {
tvCardLitDescription.setVisibility(View.VISIBLE);
tvCardLitDescription.setText(descriptionList.get(position));
} else {
tvCardLitDescription.setVisibility(View.GONE);
}
tvCardLitTitle.setText(titleList.get(position));
tvCardListPubDate.setText(pubDateList.get(position));
// imgCardLitImage.getLayoutParams().width = (int) (370 * mDensity +
// 0.5f);
// imgCardLitImage.getLayoutParams().height = (int) (220 * mDensity
// + 0.5f);
/* Loading Card list Images */
try {
new ImageLoader(context, imageSize).DisplayImage(
thumbnailList.get(position), imgCardLitImage);
} catch (Exception e) {
e.printStackTrace();
}
}
return view;
}
}
I have a project where I use a horizontal recycler view and I want to center one element. My implementation works, but not in every case check this GIF here:
As you may note it scrolls correctly if I come from the left. If I come from the right it overscrolls a lot and I have no idea how to stop nor how to fix that.
I striped my code to this example here:
public class DemoActivity extends ActionBarActivity implements View.OnClickListener {
private static final int JUMP_TO_LEFT = MyAdapter.NON_VISIBLE_ITEMS + MyAdapter.VISIBLE_ITEMS - 1;
private static final int JUMP_TO_RIGHT = MyAdapter.NON_VISIBLE_ITEMS;
private LinearLayoutManager mLayoutManager;
private RecyclerView mRecycler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
findViewById(android.R.id.button1).setOnClickListener(this);
mRecycler = (RecyclerView)findViewById(R.id.recycler);
MyAdapter mAdapter = new MyAdapter();
mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mRecycler.setLayoutManager(mLayoutManager);
mRecycler.setHasFixedSize(true);
mRecycler.scrollToPosition(MyAdapter.NON_VISIBLE_ITEMS);
mRecycler.setAdapter(mAdapter);
}
#Override
public void onClick(View v) {
int pos = mLayoutManager.findFirstVisibleItemPosition();
int outer = (MyAdapter.VISIBLE_ITEMS - 1) / 2;
if(pos + outer >= MyAdapter.ITEM_IN_CENTER) {
mRecycler.smoothScrollToPosition(JUMP_TO_RIGHT);
} else {
mRecycler.smoothScrollToPosition(JUMP_TO_LEFT);
}
}
}
And here is my adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.Holder> implements View.OnClickListener {
public static final int VISIBLE_ITEMS = 7;
public static final int NON_VISIBLE_ITEMS = 150;
private static final int TOTAL_ITEMS = VISIBLE_ITEMS + NON_VISIBLE_ITEMS * 2;
public static final int ITEM_IN_CENTER = (int)Math.ceil(VISIBLE_ITEMS / 2f) + NON_VISIBLE_ITEMS;
private Calendar mCalendar;
public MyAdapter() {
mCalendar = GregorianCalendar.getInstance();
setHasStableIds(true);
}
private int getToday() {
return (int)TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis());
}
#Override
public int getItemCount() {
return TOTAL_ITEMS;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
final TextView tv = new TextView(parent.getContext());
int width = parent.getWidth() / VISIBLE_ITEMS;
tv.setLayoutParams(new TableRow.LayoutParams(width, ViewGroup.LayoutParams.MATCH_PARENT, 1));
tv.setGravity(Gravity.CENTER);
tv.setBackgroundColor(Color.TRANSPARENT);
DisplayMetrics metrics = tv.getContext().getResources().getDisplayMetrics();
float padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, metrics);
tv.setLineSpacing(padding, 1f);
tv.setPadding(0, (int)padding, 0, 0);
tv.setOnClickListener(this);
return new Holder(tv);
}
#Override
public void onBindViewHolder(Holder holder, int position) {
int today = getToday();
mCalendar.setTimeInMillis(System.currentTimeMillis());
mCalendar.set(Calendar.HOUR_OF_DAY, 12); // set to noon to avoid energy saver time problems
mCalendar.add(Calendar.DAY_OF_YEAR, position - ITEM_IN_CENTER + 1);
DateFormat format = new SimpleDateFormat("E\nd");
String label = format.format(mCalendar.getTime()).replace(".\n", "\n");
int day = (int)TimeUnit.MILLISECONDS.toDays(mCalendar.getTimeInMillis());
holder.update(day, today, label);
}
#Override
public long getItemId(int position) {
mCalendar.setTimeInMillis(System.currentTimeMillis());
mCalendar.set(Calendar.HOUR_OF_DAY, 12); // set to noon to avoid energy saver time problems
mCalendar.add(Calendar.DAY_OF_YEAR, position - ITEM_IN_CENTER + 1);
DateFormat format = new SimpleDateFormat("dMMyyyy");
return Long.parseLong(format.format(mCalendar.getTime()));
}
#Override
public void onClick(View v) {
String day = ((TextView)v).getText().toString().replace("\n", " ");
Toast.makeText(v.getContext(), "You clicked on " + day, Toast.LENGTH_SHORT).show();
}
public class Holder extends RecyclerView.ViewHolder {
private final Typeface font;
private Holder(TextView v) {
super(v);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
font = Typeface.create("sans-serif-light", Typeface.NORMAL);
} else {
font = null;
}
}
public void update(int day, int today, String label) {
TextView tv = (TextView)itemView;
tv.setText(label);
if(day == today) {
tv.setTextSize(18);
tv.setTypeface(null, Typeface.BOLD);
} else {
tv.setTextSize(16);
tv.setTypeface(font, Typeface.NORMAL);
}
tv.setBackgroundColor(0xff8dc380);
}
}
}
Do you see a reason for that? To make it simpler for you I also put this code on GitHub. https://github.com/rekire/RecylcerViewBug
I found a surprising simple workaround:
#Override
public void onClick(View v) {
int pos = mLayoutManager.findFirstVisibleItemPosition();
int outer = (MyAdapter.VISIBLE_ITEMS + 1) / 2;
int delta = pos + outer - ForecastAdapter.ITEM_IN_CENTER;
//Log.d("Scroll", "delta=" + delta);
View firstChild = mForecast.getChildAt(0);
if(firstChild != null) {
mForecast.smoothScrollBy(firstChild.getWidth() * -delta, 0);
}
}
Here I calculate the width to jump myself, that does exactly what I want.
In case of LinearLayoutManager with vertical orientation you can create own SmoothScroller and override calculateDyToMakeVisible() method where you can set desired view position. For example, to make the target view always to be appeared at the top side of RecyclerView after smoothScroll() write this:
class CustomLinearSmoothScroller extends LinearSmoothScroller {
public CustomLinearSmoothScroller(Context context) {
super(context);
}
#Override
public int calculateDyToMakeVisible(View view, int snapPreference) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (!layoutManager.canScrollVertically()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)view.getLayoutParams();
final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
final int viewHeight = bottom - top;
final int start = layoutManager.getPaddingTop();
final int end = start + viewHeight;
return calculateDtToFit(top, bottom, start, end, snapPreference);
}
"top" and "bottom" - bounds of the target view
"start" and "end" - points between which the view should be placed during smoothScroll
This worked for me:
itemsView.smoothScrollBy(-recyclerView.computeHorizontalScrollOffset(), 0)
To support smooth scrolling, you must override
smoothScrollToPosition(RecyclerView, State, int) and create a
RecyclerView.SmoothScroller.
RecyclerView.LayoutManager is responsible
for creating the actual scroll action. If you want to provide a custom
smooth scroll logic, override smoothScrollToPosition(RecyclerView,
State, int) in your LayoutManager.
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#smoothScrollToPosition(int)
In your case, use smoothScrollBy could be a workaround (doesn't need this override).
I'm creating an app which should list some snowboard tricks and when one clicks on a trick, a popup should appear with information on the trick and how to do it (with text and maybe a youtube vid).
How does one realise this?
Here is a code for Creating a custom list adapter with a popup window that pop up when you press a certain image in the ListView row:
Here is the Adapter:
public class tasksRepositoryAdapter extends ArrayAdapter<Task>
{
private ArrayList<Task> list;
public tasksRepositoryAdapter(Context context, int textViewResourceId, List<Task> tasksRepository)
{
super(context, textViewResourceId, tasksRepository);
this.list = new ArrayList<Task>();
for (Task task : tasksRepository)
{
this.list.add(task);
}
}
public View getView(final int position, View convertView, ViewGroup parent)
{
View row;
final ViewHolder holder = new ViewHolder();
tfRobotoRegular = Typeface.createFromAsset(getAssets(),"Roboto-Regular.ttf");
LayoutInflater inflator = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflator.inflate(R.layout.new_row, null);
holder.tvTitle = (TextView) row.findViewById(R.id.text_title);
String title = tasksRepository.get(position).getTitle();
if (title.length()>25)
{
title = title.substring(0, 24);
title = title + "...";
}
holder.tvTitle.setText(title);
holder.tvTitle.setTypeface(tfRobotoRegular);
holder.tvDate = (TextView) row.findViewById(R.id.text_date);
holder.tvDate.setText(tasksRepository.get(position).getDate());
holder.tvDate.setTypeface(tfRobotoRegular);
holder.tvTime = (TextView) row.findViewById(R.id.text_time);
holder.tvTime.setText(tasksRepository.get(position).getTime());
holder.tvTime.setTypeface(tfRobotoRegular);
holder.tvDescription = (TextView) row.findViewById(R.id.text_description);
String description = tasksRepository.get(position).getDescription();
if (description.length()>46)
{
description = description.substring(0, 45);
description = description + "...";
}
holder.tvDescription.setText(description);
holder.tvDescription.setTypeface(tfRobotoRegular);
holder.tvId = (TextView) row.findViewById(R.id.text_id);
holder.tvId.setText(String.valueOf(tasksRepository.get(position).getId()));
holder.tvId.setTypeface(tfRobotoRegular);
holder.tvLocation = (TextView) row.findViewById(R.id.text_location);
holder.tvLocation.setText(tasksRepository.get(position).getCity());
holder.llRowLayout = (LinearLayout) row.findViewById(R.id.llRowLayout);
holder.imCalendar = (ImageView) row.findViewById(R.id.iCalendar);
holder.imClock = (ImageView) row.findViewById(R.id.iClock);
holder.imLocation = (ImageView) row.findViewById(R.id.iLocation);
holder.imTaskStatusButton = (ImageView) row.findViewById(R.id.iTaskStatusButton);
holder.imTaskStatusButton.setTag(position);
holder.imTaskStatusButton.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
int[] location = new int[2];
currentRowId = position;
currentRow = v;
// Get the x, y location and store it in the location[] array
// location[0] = x, location[1] = y.
v.getLocationOnScreen(location);
//Initialize the Point with x, and y positions
point = new Point();
point.x = location[0];
point.y = location[1];
showStatusPopup(TasksListActivity.this, point);
}
});
String status = tasksRepository.get(position).getStatus();
Log.d(TAG, "The status of the current row: "+ status );
setStatusColorImages(status, holder.imClock, holder.imCalendar, holder.imLocation, holder.llRowLayout);
return row;
}
}
Here is the ViewHolder:
static class ViewHolder
{
RelativeLayout rlTitle;
LinearLayout llRowLayout;
TextView tvId;
TextView tvTitle;
TextView tvDate;
TextView tvTime;
TextView tvDescription;
TextView tvLocation;
ImageView imClock;
ImageView imCalendar;
ImageView imLocation;
ImageView imTaskStatusButton;
}
And the PopUp Window:
// The method that displays the popup.
private void showStatusPopup(final Activity context, Point p) {
// Inflate the popup_layout.xml
LinearLayout viewGroup = (LinearLayout) context.findViewById(R.id.llStatusChangePopup);
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = layoutInflater.inflate(R.layout.status_popup_layout, null);
// Creating the PopupWindow
changeStatusPopUp = new PopupWindow(context);
changeStatusPopUp.setContentView(layout);
changeStatusPopUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);
changeStatusPopUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
changeStatusPopUp.setFocusable(true);
// Some offset to align the popup a bit to the left, and a bit down, relative to button's position.
int OFFSET_X = -20;
int OFFSET_Y = 50;
//Clear the default translucent background
changeStatusPopUp.setBackgroundDrawable(new BitmapDrawable());
// Displaying the popup at the specified location, + offsets.
changeStatusPopUp.showAtLocation(layout, Gravity.NO_GRAVITY, p.x + OFFSET_X, p.y + OFFSET_Y);
}