get recycler item absolute coordinates - android

I am trying to make the items in a recyclerview to move when scrolling up or down in a matter which depends on the the Y coordinate of the child views. However I can't seem to be able to get the coordinate of the children.
How can I get the Y coordinate?
I need it in the onBindViewHolder method so that I can set the layout param of the child view according to it.

in your Activity/Fragment :
RecyclerView yourRecyclerView;
FansAdapter yourRecyclerAdapter;
........
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
yourRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
yourRecyclerView.setLayoutManager(new LinearLayoutManager(this));
yourRecyclerView.setHasFixedSize(true);
yourRecyclerAdapter = new YourAdapter (populateYourList(), this);
yourRecyclerView.setAdapter(yourRecyclerAdapter);
yourRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
View firstVisibleChild = recyclerView.getChildAt(0);//change if necessary. determine that item(items) you need to find
final ImageView imageView = (ImageView) firstVisibleChild.findViewById(R.id.item_image);
if (null != imageView) {
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int[] locationsFrom = new int[2];
imageView.getLocationInWindow(locationsFrom);
int xStart = locationsFrom[0];
int yStart = locationsFrom[1];
Log.d("LOG_TAG", "your item xEnd=" + xStart +" yEnd=" + yStart);
if (null != yourRecyclerAdapter) {
yourRecyclerAdapter.setX(xStart);
yourRecyclerAdapter.setY(yStart);
}
}
}
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
if (null != yourRecyclerView) {
yourRecyclerView.clearOnScrollListeners();
}
}
and Adapter:
public class YourAdapter extends RecyclerView.Adapter<YourAdapter.YouViewHolder> {
ArrayList<String> fansList;
Context mContext;
private int x;
private int y;
public YourAdapter(ArrayList<String> fansList, Context context) {
this.fansList = fansList;
this.mContext = context;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
#Override
public YouViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if (viewGroup instanceof RecyclerView) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycle_item_fans, viewGroup, false);
view.setFocusable(true);
return new YouViewHolder(view);
} else {
throw new RuntimeException("Not bound to RecyclerView");
}
}
#Override
public void onBindViewHolder(YouViewHolder holder, int position) {
holder.itemName.setText(fansList.get(position));
Log.v("LOG_TAG","x="+x+" y="+y);// use y or x
}
#Override
public int getItemCount() {
return fansList.size();
}
//holder
public final class YouViewHolder extends RecyclerView.ViewHolder {
public final TextView itemName;
public final ImageView imageView;
public YouViewHolder(View rowView) {
super(rowView);
this.itemName = (TextView) rowView.findViewById(R.id.text_fan_name);
this.imageView = (ImageView) rowView.findViewById(R.id.image_second_free);
}
}
}

Related

recyclerview vieholder is not getting recycled

developing an image picker using recyclerview with all images in devices for an android app, facing an issue when user scroll through images its memory usage keeps increasing eventually if lots of images there is oom exception, have searched for this issue over the internet, have applied several techniques but it is of little use, i have seen other app(whatsapp, olx) its picker scrolling is nice and no oom, i also tried dump heap in android studio to detect memory leakage, i found that there are as many viewholders as number of images, its not being recycled, am not able to find the reason, please help me in detecting the issue, attaching code..
/** adapter class for grid recyclerview*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<CreateList> galleryList;
private Context context;
private List<String> imageUrls = new ArrayList<>(3);
private List<Integer> positionList = new ArrayList<>(3);
int count = 0;
RequestOptions options;
RequestBuilder glide;
RequestManager glideMain;
int imgWidth;
public MyAdapter(Context context, ArrayList<CreateList> galleryList, RequestOptions options, RequestBuilder<Bitmap> glide, RequestManager glideMain, int imgWidth) {
this.galleryList = galleryList;
this.context = context;
this.options = options;
this.glide = glide;
this.glideMain = glideMain;
this.imgWidth = imgWidth;
}
#Override
public void onViewRecycled(#NonNull ViewHolder holder) {
holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);
holder.img.setImageDrawable(null);
if(holder.title.getText().toString().equals("1")){
}
//holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewRecycled(holder);
}
#Override
public void onViewDetachedFromWindow(#NonNull ViewHolder holder) {
/* holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);*/
holder.img.setImageDrawable(null);
// holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewDetachedFromWindow(holder);
}
#Override
public void onViewAttachedToWindow(#NonNull final ViewHolder holder) {
/* holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.handleThumbnail(view);
}
});*/
super.onViewAttachedToWindow(holder);
}
public ArrayList<CreateList> getGalleryList() {
return galleryList;
}
public void setGalleryList(ArrayList<CreateList> galleryList) {
this.galleryList = galleryList;
}
public List<Integer> getPositionList() {
return positionList;
}
public void setPositionList(List<Integer> positionList) {
this.positionList = positionList;
}
public List<String> getImageUrls() {
return imageUrls;
}
public void setImageUrls(List<String> imageUrls) {
this.imageUrls = imageUrls;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_layout, viewGroup, false);
//img.setOnClickListener();
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, int pos) {
glide
.load(galleryList.get(pos).getImage_Location())
.thumbnail(0.1F)
.apply(options)
.into(viewHolder.img);
if (positionList.contains(pos)){
// view not selected
//viewHolder.parent.setBackgroundColor(Color.LTGRAY);
viewHolder.title.setVisibility(View.VISIBLE);
viewHolder.title.setText(String.valueOf(positionList.indexOf(pos)+1));
viewHolder.img.setColorFilter(viewHolder.getColorWithAlpha(Color.GREEN, 0.3f));
}
}
#Override
public int getItemCount() {
return galleryList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private ImageView img;
public ViewHolder(View view) {
super(view);
title = (TextView)view.findViewById(R.id.highlightText);
img = (ImageView) view.findViewById(R.id.img);
/* view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleThumbnail(v);
}
});*/
/* img = view.findViewById(R.id.img);*/
img.setLayoutParams(new ConstraintLayout.LayoutParams(imgWidth, imgWidth));
/*img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// This is OnClick of any list Item
handleThumbnail(v);
}
});*/
/* img.setOnTouchListener(new View.OnTouchListener() {
private Rect rect;
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
img.setColorFilter(Color.argb(50, 0, 0, 0));
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
}
if(event.getAction() == MotionEvent.ACTION_UP){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
}
return false;
}
});*/
}
public void handleThumbnail(View v) {
if(!imageUrls.contains(galleryList.get(getAdapterPosition()).getImage_Location())) {
if(count<3) {
positionList.add(getAdapterPosition());
String selectedImgUrl = galleryList.get(getAdapterPosition()).getImage_Location();
imageUrls.add(selectedImgUrl);
//img.setBackground(context.getResources().getDrawable(R.drawable.button_background_checked));
img.setColorFilter(getColorWithAlpha(Color.GREEN, 0.3f));
//title.setVisibility(View.VISIBLE);
//title.setText(String.valueOf(count+1));
//View viewSelected = gridRecycler.findViewHolderForAdapterPosition(position).itemView;
title.setVisibility(View.VISIBLE);
title.setText(String.valueOf(count+1));
count++;
} else {
Toast.makeText().show();
}
} else {
imageUrls.remove(galleryList.get(getAdapterPosition()).getImage_Location());
positionList.remove(new Integer(getAdapterPosition()));
//imageUrls.remove( Integer.parseInt(title.getText().toString()));
title.setVisibility(View.GONE);
count--;
// img.setBackground(context.getResources().getDrawable(R.drawable.button_background));
img.setColorFilter(Color.argb(0, 0, 0, 0));
EventBus.getDefault().post(new MessageEvent(imageUrls, positionList));
}
}
public int getColorWithAlpha(int color, float ratio) {
int newColor = 0;
int alpha = Math.round(Color.alpha(color) * ratio);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
newColor = Color.argb(alpha, r, g, b);
return newColor;
}
}
activity code using background thread to load data and set in adapter:
private class PrepareData extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int imgWidth = (int) (displayMetrics.widthPixels*0.32);
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(imgWidth/3)
.dontAnimate()
.centerCrop()
.skipMemoryCache(true);
final RequestManager glide = Glide.with(gridRecycler.getContext());
RequestBuilder builder = glide.asBitmap();
gridRecycler.setHasFixedSize(true);
final RecyclerView.LayoutManager layoutManager = new GridLayoutManager(gridRecycler.getContext(), 3);
createLists = getAllFolderImages(this);
adapter = new MyAdapter(this, createLists, options, builder, glide, imgWidth);
gridRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
glide.resumeRequests();
}
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || newState==AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
glide.pauseRequests();
System.gc();
}
}
});
runOnUiThread(new Runnable() {
#Override
public void run() {
gridRecycler.setLayoutManager(layoutManager);
gridRecycler.setAdapter(adapter);
//put the code here that is giving exception
}
});
return null;
}
}
android profiling screen-shot, its visible that viewholder is consuming lots of memory and has lots of instances.

Calculating total price of items in a List of Items in a recyclerview

I want to calculate the price of items in a List and store the value in sharedpreference. Each Item has a price which takes the form of a double.The issue is when i use my method, it only takes into account one item in the list and calculates for that value. I am using a NumberPicker to change the quantity of the items.
Here is my adapter for handling the List of items that will be displayed on the RecyclerView:
public class CartAdapter extends RecyclerView.Adapter<CartAdapter.ViewHolder> {
private List<SingleItem> items;
private SessionManager sessionManager;
private Context context;
private int pos;
public CartAdapter() {
}
public CartAdapter(Context context, List<SingleItem> items) {
this.items = items;
this.context = context;
sessionManager = new SessionManager(context);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cartitem, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final SingleItem singleItem = items.get(position);
holder.tv_title.setText(singleItem.getTitle());
holder.tv_price.setText("Ksh: " + singleItem.getPrice());
Picasso.with(context).load(singleItem.getUrl()).fit().into(holder.imgcart);
holder.numcart.setMinValue(1);
holder.numcart.setMaxValue(15);
holder.numcart.setWrapSelectorWheel(false);
int qty = holder.numcart.getValue();
getTotal(qty, singleItem.getPrice());
}
public double getTotal(int value, double amount){
double totalamount;
double amountall = amount;
int quantity = value;
totalamount = amountall * quantity;
sessionManager.grandtotal("Ksh: " + totalamount);
return totalamount;
}
#Override
public int getItemCount() {
return items.size();
}
public void removeItem(SingleItem item) {
sessionManager.removeitem(context,item);
items.remove(item);
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tv_price;
TextView tv_title;
NumberPicker numcart;
ImageView imgcart;
public ViewHolder(View view) {
super(view);
tv_price = (TextView) view.findViewById(R.id.titlecart);
tv_title = (TextView) view.findViewById(R.id.pricecart);
numcart = (NumberPicker) view.findViewById(R.id.pickercart);
imgcart = (ImageView) view.findViewById(R.id.imgcart);
}
}
}
Here is how i'm displaying the RecyclerView on the fragment:
public class Details extends Fragment {
private RecyclerView RecyclerDetails;
private TextView CartPrice;
private CheckBox CheckCart;
private List<SingleItem> list;
private CartAdapter adapter;
private boolean add = false;
private Paint p = new Paint();
private SessionManager sessionManager;
private int pos;
public Details() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_details, container, false);
RecyclerDetails = (RecyclerView) view.findViewById(R.id.recyclercart);
CartPrice = (TextView) view.findViewById(R.id.tvcarttotal);
CheckCart = (CheckBox) view.findViewById(R.id.chkcart);
sessionManager = new SessionManager(getContext());
Toasty.info(getContext(),"Swipe to go to Next", Toast.LENGTH_SHORT,true).show();
RecyclerDetails.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
RecyclerDetails.setLayoutManager(layoutManager);
list = sessionManager.getItems(getContext());
HashMap<String,String> map = sessionManager.itemstostring();
String data = map.get(SessionManager.KEY_ITEMS);
Log.i(Constants.TAG,data);
HashMap<String,String> tot = sessionManager.getgrandtotal();
String total = tot.get(SessionManager.KEY_TOTAL);
CartPrice.setText(total);
CheckCart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (((CheckBox) v).isChecked()){
sessionManager.saveditems("true");
} else {
sessionManager.saveditems("false");
}
}
});
RecyclerDetails.addOnItemTouchListener(new RecyclerItemClickListener(getContext(), new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
}));
if(list != null){
adapter = new CartAdapter(getContext(),list);
RecyclerDetails.setAdapter(adapter);
}
initswipe();
return view;
}
private void initswipe() {
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
if (direction == ItemTouchHelper.RIGHT) {
adapter.removeItem(list.get(position));
sessionManager.saveitems(getContext(),list);
}
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
Bitmap icon;
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
View itemView = viewHolder.itemView;
float height = (float) itemView.getBottom() - (float) itemView.getTop();
float width = height / 3;
if(dX > 0){
p.setColor(Color.parseColor("#14a895"));
RectF background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX,(float) itemView.getBottom());
c.drawRect(background,p);
icon = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_delete);
RectF icon_dest = new RectF((float) itemView.getLeft() + width ,(float) itemView.getTop() + width,(float) itemView.getLeft()+ 2*width,(float)itemView.getBottom() - width);
c.drawBitmap(icon,null,icon_dest,p);
} else {
p.setColor(Color.parseColor("#14a895"));
RectF background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(),(float) itemView.getRight(), (float) itemView.getBottom());
c.drawRect(background,p);
icon = BitmapFactory.decodeResource(getResources(),android.R.drawable.ic_menu_delete);
RectF icon_dest = new RectF((float) itemView.getRight() - 2*width ,(float) itemView.getTop() + width,(float) itemView.getRight() - width,(float)itemView.getBottom() - width);
c.drawBitmap(icon,null,icon_dest,p);
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(RecyclerDetails);
}
}
And finally, here is the function i use to store the value of the total of all items in the list
public void grandtotal (String total){
editor.putString(KEY_TOTAL,total);
editor.commit();
}
public HashMap<String, String> getgrandtotal(){
HashMap<String, String> tot = new HashMap<>();
tot.put(KEY_TOTAL,pref.getString(KEY_TOTAL,null));
return tot;
}
Is there a function I haven't included in the fragment where I'm displaying the RecyclerView with the items ?
Is it just a small change needed ?
private int grandTotal(List<SingleItem> items){
int totalPrice = 0;
for(int i = 0 ; i < items.size(); i++) {
totalPrice += items.get(i).getPrice();
}
return totalPrice;
}
this is the method u need to implement to get the grand total price. U can call this method when u r adding an item or removing and item from the list.
This function in kotlin
var total = 0;
for ( i in 0 until myDebt.size){
total += myDebt[i].valueAdp
}
this is the method you need to implement to get the grand total price. can call this method within your activity
 and you can still assign the value to textView.
mtv_my_debt.setText(String.valueOf(total))

android - RecyclerView item not clickable after drag and drop in Nougat (Android 7.0)

I'm facing this issue only on android Nougat. After drag and dropping an item, the OnClick event doesn't fire on this item anymore.
Here's the adapter :
public class VideoListAdapter extends RecyclerView.Adapter<VideoListAdapter.VideoListViewHolder> implements ItemTouchHelperAdapter {
private List<Media> videos;
private LayoutInflater inflater;
private Context context;
private int selectedItemPosition;
private Media selectedMedia;
private static final int NOTHING_SELECTED = -1;
public VideoListAdapter(Context context, List<Media> videos, Delegate delegate) {
inflater = LayoutInflater.from(context);
setVideos(videos);
this.context = context;
setSelectedItem(NOTHING_SELECTED);
}
public void setSelectedItem(int pos) {
selectedItemPosition = pos;
if(pos != NOTHING_SELECTED)
selectedMedia = videos.get(pos);
else
selectedMedia = null;
}
public void setVideos(List<Media> videos) {
this.videos = videos;
setSelectedItem(NOTHING_SELECTED);
}
private void onVideosReordered() {
if(selectedMedia != null)
setSelectedItem(videos.indexOf(selectedMedia));
}
public void unselectAll() {
setSelectedItem(NOTHING_SELECTED);
notifyDataSetChanged();
}
#Override
public VideoListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.item_video_list, parent, false);
return new VideoListViewHolder(view);
}
#Override
public void onBindViewHolder(final VideoListViewHolder holder, int position) {
String imagePath = videos.get(position).getFilePath();
boolean isEmptyItem = imagePath == null || imagePath.isEmpty();
if (isEmptyItem) {
holder.video_list_item_selected.setVisibility(View.GONE);
holder.img_image.setVisibility(View.GONE);
} else {
UtilsMedia.loadThumbnailImageNoSpinAnimation(holder.img_image, imagePath);
boolean isSelected = selectedItemPosition == position;
if (isSelected && !isEmptyItem)
holder.video_list_item_selected.setVisibility(View.VISIBLE);
else
holder.video_list_item_selected.setVisibility(View.GONE);
}
}
#Override
public int getItemCount() {
return videos.size();
}
#Override
public boolean onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++)
Collections.swap(videos, i, i + 1);
} else {
for (int i = fromPosition; i > toPosition; i--)
Collections.swap(videos, i, i - 1);
}
notifyItemMoved(fromPosition, toPosition);
return true;
}
#Override
public void onItemDropped() {
onVideosReordered();
notifyDataSetChanged();
}
class VideoListViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
public ImageView img_image;
public ImageView video_list_item_selected;
public FrameLayout fl_root;
public Context context;
public boolean isSelected;
private static final String TAG = "VideoListViewHolder";
public VideoListViewHolder(View view) {
super(view);
img_image = (ImageView) view.findViewById(R.id.img_image);
video_list_item_selected = (ImageView) view.findViewById(R.id.video_list_item_selected);
fl_root = (FrameLayout) view.findViewById(R.id.fl_root);
context = view.getContext();
isSelected = false;
fl_root.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int currentPosition = getAdapterPosition();
if (selectedItemPosition == currentPosition) {
setSelectedItem(NOTHING_SELECTED);
} else {
if (selectedItemPosition != -1)
notifyItemChanged(selectedItemPosition);
setSelectedItem(currentPosition);
}
notifyItemChanged(currentPosition);
}
});
}
#Override
public void onItemSelected() {
final Animation zoomOut = AnimationUtils.loadAnimation(context, R.anim.zoom_out);
zoomOut.setDuration(300);
fl_root.startAnimation(zoomOut);
}
#Override
public void onItemClear() {
final Animation zoomIn = AnimationUtils.loadAnimation(context, R.anim.zoom_in);
zoomIn.setDuration(300);
fl_root.startAnimation(zoomIn);
}
}
}
And here is the item touch helper callback :
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f;
protected final ItemTouchHelperAdapter mAdapter;
public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
mAdapter = adapter;
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
private int moveSource = -1;
private int moveTarget = -1;
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType())
return false;
if(moveSource == -1)
moveSource = source.getAdapterPosition();
moveTarget = target.getAdapterPosition();
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
super.onSelectedChanged(viewHolder, actionState);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setAlpha(ALPHA_FULL);
if (viewHolder instanceof ItemTouchHelperViewHolder) {
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
if(moveSource != -1 && moveTarget != -1 && moveSource != moveTarget)
mAdapter.onItemDropped();
moveSource = moveTarget = -1;
}
}
Any help would be appreciated, thanks !
I had to remove every notifyDataSet changed and replace them by other notifyXXX (notifyItemRangeInserted, notifyItemRangeRemoved, notifyItemRangeChanged, etc)

Android implementing ScrollView setOnScrollChangeListener

in my application for show and hide some widgets i found this below code on this site, but i cant implementing that on my fragment, for example:
public abstract class HidingScrollListener extends RecyclerView.OnScrollListener {
private static final int HIDE_THRESHOLD = 20;
private int mScrolledDistance = 0;
private boolean mControlsVisible = true;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
if (firstVisibleItem == 0) {
if(!mControlsVisible) {
onShow();
mControlsVisible = true;
}
} else {
if (mScrolledDistance > HIDE_THRESHOLD && mControlsVisible) {
onHide();
mControlsVisible = false;
mScrolledDistance = 0;
} else if (mScrolledDistance < -HIDE_THRESHOLD && !mControlsVisible) {
onShow();
mControlsVisible = true;
mScrolledDistance = 0;
}
}
if((mControlsVisible && dy>0) || (!mControlsVisible && dy<0)) {
mScrolledDistance += dy;
}
}
public abstract void onHide();
public abstract void onShow();
}
My Fragment:
public class FragmentMarketDetail extends Fragment implements ObservableHorizontalScrollView.OnScrollListener {
private ScrollView scrollViewTest;
private Context context;
public static FragmentMarketDetail newInstance() {
FragmentMarketDetail fragmentFirst = new FragmentMarketDetail();
return fragmentFirst;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_online_categories, container, false);
scrollViewTest = (ScrollView) view.findViewById(R.id.scrollViewTest);
scrollViewTest.setOnScrollChangeListener(context);
return view;
}
#Override
public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY) {
Log.e("-----> onScrollChanged", x + "");
}
#Override
public void onEndScroll(ObservableHorizontalScrollView scrollView) {
Log.e("----->onEndScroll ", "");
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
context = activity;
}
}
I get error for this line: scrollViewTest.setOnScrollChangeListener(context);
You cannot set a onScrollChangedListener to a ScrollView directly below API23.
you may however use this
scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollX = rootScrollView.getScrollX(); //for horizontalScrollView
int scrollY = rootScrollView.getScrollY(); //for verticalScrollView
//DO SOMETHING WITH THE SCROLL COORDINATES
}
});
see onScrollListener for a ScrollView
Edit
scrollViewTest.setOnScrollChangeListener(context);
In the above line you are attempting to set the Context as a listener which is not possible, as you Fragment implements ObservableHorizontalScrollView.OnScrollListener you should call
scrollViewTest.setOnScrollChangeListener(this);

Get center visible item of RecycleView when scrolling

This is what I want:
As image above, I want to draw a center line on RecycleView, then get the center item when scrolling (as well as move left or right)
Here is my try to draw a horizontal RecycleView:
HorizontalAdapter adapter = new HorizontalAdapter(data);
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recycleView.setLayoutManager(layoutManager);
recycleView.setAdapter(adapter);
Is there any way to know which item is moved to the center of RecycleView? And how can I scroll RecycleView to left or right just one position?
Update: I tried to use a scroll listener to get the middle position, but it doesn't work as an aspect.
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstPos = layoutManager.findFirstVisibleItemPosition();
int lastPos = layoutManager.findLastVisibleItemPosition();
int middle = Math.abs(lastPos - firstPos) / 2 + firstPos;
int selectedPos = -1;
for (int i = 0; i < adapter.getItemCount(); i++) {
if (i == middle) {
adapter.getItem(i).setSelected(true);
selectedPos = i;
} else {
adapter.getItem(i).setSelected(false);
}
}
adapter.notifyDataSetChanged();
}
And get the result:
I only want to change the selected item (make text to white color) when it is on the blue Rect
I made something just like this. I can do exactly what you need.
First of all, this is how is my alogrithm work
This is my recyclerView Adapter
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_date,
parent, false);
return new DateViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_padding,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new DateViewHolder(view);
}
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
if(labelerDate.dateType.equals(BirthDayActivity.DateType.C31))
holder.tvDate.setText(String.valueOf(labelerDate.valueDate));
holder.tvDate.setVisibility(View.VISIBLE);
holder.imgSmall.setVisibility(View.VISIBLE);
if (position == selectedItem) {
holder.tvDate.setTextColor(Color.parseColor("#094673"));
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.textviewbold);
} else {
holder.tvDate.setTextColor(Color.GRAY);
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.gray);
}
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.dateType.equals(BirthDayActivity.DateType.NONE)) {
return VIEW_TYPE_PADDING;
}
return VIEW_TYPE_ITEM;
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public ImageView imgSmall;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.tvNumberDate);
imgSmall = (ImageView) itemView.findViewById(R.id.small_marked_dob);
}
}}
This is most important alogrithm:
public void getRecyclerviewDate() {
recyclerViewDate = (RecyclerView) findViewById(R.id.recyclerViewDay);
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate ;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if(newState == RecyclerView.SCROLL_STATE_IDLE){
calculatePositionAndScrollDate(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (labelerDates == null)
labelerDates = new ArrayList<>();
labelerDates.addAll(genLabelerDate(currentMonth, currentYear));
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
return true;
}
});
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPositionDate(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
}
private void setDateValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
setColorDate = expectedPositionDateColor + 1;
//set color here
dateAdapter.setSelecteditem(setColorDate);
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
allPixelsDate = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE);
allPixelsDateChanged = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED);
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putFloat(BUNDLE_LIST_PIXELS_DATE, allPixelsDate);
outState.putFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED, allPixelsDateChanged);
}
And this is my result:
Look at this video link, this is my app demo
Sometimes is needed the entire example code block together, because we may miss something. Here is what I have, feel free to correct anything since I may be doing some little mistake somewhere. And Yes, this answer is an extension of #tranhieu answer. Thanks #tranhieu.
MainActivity.java
package com.test;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
public float firstItemWidthDate;
public float paddingDate;
public float itemWidthDate;
public int allPixelsDate;
public int finalWidthDate;
private DateAdapter dateAdapter;
private ArrayList<LabelerDate> labelerDates = new ArrayList<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getRecyclerviewDate();
}
public void getRecyclerviewDate() {
final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
if (recyclerViewDate != null) {
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
setDateValue();
}
}, 300);
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
setDateValue();
}
}, 5000);
}
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
calculatePositionAndScrollDate(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (labelerDates == null) {
labelerDates = new ArrayList<>();
}
genLabelerDate();
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
return true;
}
});
}
private void genLabelerDate() {
for (int i = 0; i < 32; i++) {
LabelerDate labelerDate = new LabelerDate();
labelerDate.setNumber(Integer.toString(i));
labelerDates.add(labelerDate);
if (i == 0 || i == 31) {
labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
} else {
labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
}
}
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPositionDate(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
setDateValue();
}
//
private void setDateValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
// set color here
dateAdapter.setSelecteditem(setColorDate);
}
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
parent, false);
return new DateViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new DateViewHolder(view);
}
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(labelerDate.getNumber());
holder.tvDate.setVisibility(View.VISIBLE);
Log.d(TAG, "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.d(TAG, "center" + position);
holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
holder.tvDate.setTextSize(35);
} else {
holder.tvDate.setTextColor(Color.WHITE);
holder.tvDate.setTextSize(18);
}
} else {
holder.tvDate.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.txt_date);
}
}
}
private class LabelerDate {
private int type;
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_tasks_date"
android:layout_width="match_parent"
android:layout_height="48dp" />
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_marginTop="48dp"
android:src="#android:drawable/ic_dialog_info" />
</FrameLayout>
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="#+id/txt_date"
android:layout_width="#dimen/item_dob_width"
android:layout_height="48dp"
android:text="32"
android:textColor="#android:color/white"
android:background="#android:color/darker_gray"
android:textSize="28sp"
android:gravity="center"/>
</LinearLayout>
dimens.xml
<resources>
<dimen name="item_dob_width">100dp</dimen>
</resources>
Oh boy. I've been searching for this answer for almost a week and then found out the solution. Custom LayoutManagers? No. ItemDecorator? Nope.
Here's the easiest way to do it:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingStart="150dp"
android:paddingEnd="150dp"
android:clipToPadding="false" />
The critical part is:
android:paddingStart="150dp"
android:paddingEnd="150dp"
android:clipToPadding="false"
And then just assign SnapHelper to your RecylcerView:
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)
This is it. The easiest and most perfect solution to the problem
I'm used the SnapHelper right here:
// init snaphelper
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView)
// init layout manager
LinearLayoutManager layoutManager = new LinearLayoutManager(mainActivity);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
// init adapter
adatper.setSnapHelper(snapHelper);
adatper.setLayoutManager(layoutManager);
adatper.initAdapter(new Float((DisplayHelper.getDisplayWidth(mainActivity) / 2) - (fooViewWidth / 2)).intValue());
recyclerView.setAdapter(adatper);
As said by TranHieu the solution of inserting 2 item for padding (at start and at end positions) is good.
I don't like the use of ViewTreeObserver because of poor readability of code. With this technique then you must also manage redrawing of the items if they are recycled.
If you are using customview classes you can set its width directly into these classes.
For example this is my padding class
/**
* Created by firegloves on 25/09/15.
*/
#EViewGroup(R.layout.view_padding)
public class PaddingView extends FooView {
Context mCtx;
public PaddingView(Context context) {
super(context);
mCtx = context;
}
public void setWidth(int width) {
setLayoutParams(new LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
In my adapter I store the desired padding item width, that is equal to (displayWidth / 2) - (realItemWidth / 2)
This is my adapter, don't look at methods not matching RecyclerView.Adapter, pay attention to the initAdapter method and to the onCreateItemView method
#EBean
public class FooAdapterRecycler extends RecyclerViewAdapterBase<Foo, FooView> {
private final int TYPE_PADDING_VIEW = 0;
private final int TYPE_REAL_VIEW = 1;
#RootContext
Context ctx;
#Bean(Finder.class)
IFinder finder;
SnapHelper snapHelper;
RecyclerView.LayoutManager layoutManager;
private int paddingWidth = 0;
/**
* preleva i dati dal finder
*/
public void initAdapter(int paddingWidth) {
/*******************************
* THIS CODE IS THE IMPORTANT ONE
******************************/
this.paddingWidth = paddingWidth;
// add 1 item for initial space
mItems = new ArrayList<>();
Foo foo = new Foo();
mItems.add(foo);
// get real items from finder
mItems.addAll(finder.findAll());
// add 1 item for final space
mItems = new ArrayList<>();
Foo foo2 = new Foo();
mItems.add(foo2);
}
#Override
public int getItemViewType(int position) {
if (position == 0 || position == getItemCount()-1) {
return TYPE_PADDING_VIEW;
} else {
return TYPE_REAL_VIEW;
}
}
#Override
protected FooView onCreateItemView(ViewGroup parent, int viewType) {
/*******************************
* THIS CODE IS THE IMPORTANT ONE
******************************/
if (viewType == TYPE_PADDING_VIEW) {
PaddingView view = PaddingView_.build(ctx);
view.setWidth(paddingWidth);
return view;
} else {
return FooView_.build(ctx);
}
}
public void setSnapHelper(SnapHelper snapHelper) {
this.snapHelper = snapHelper;
}
public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
}
I'm using AndroidAnnotations library but it's not required
Hope that helps
USING SNAPHELPER - A SMOOTHER SOLUTION
Here it is another solution using SnapHelper. Starting from the answer of #TranHieu here:
https://stackoverflow.com/a/34647005/3944251
and the compressed by #sector11 here:
https://stackoverflow.com/a/38411582/3944251
I wrote the following code which is also based in both answers above, but it's simpler and offers a smoother solution using SnapHelper presented in android support library 24.2.0.
Here you have the MainActivity class. The rest is the same with #sector11's answer.
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSnapHelper;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
public float firstItemWidthDate;
public float itemWidthDate;
public int allPixelsDate;
public int finalWidthDate;
private DateAdapter dateAdapter;
private ArrayList<LabelerDate> labelerDates;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
labelerDates = new ArrayList<>();
getRecyclerviewDate();
}
public void getRecyclerviewDate() {
final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
recyclerViewDate.postDelayed(new Runnable() {
#Override
public void run() {
//recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
setDateValue();
}
}, 300);
ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerViewDate.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
firstItemWidthDate = (finalWidthDate - itemWidthDate) / 2;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerViewDate.setLayoutManager(dateLayoutManager);
/* Create a LinearSnapHelper and attach the recyclerView to it. */
final LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerViewDate);
recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
allPixelsDate += dx;
recyclerView.post(new Runnable() {
public void run() {
setDateValue();
}
});
}
});
genLabelerDate();
dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
recyclerViewDate.setAdapter(dateAdapter);
dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
return true;
}
});
}
private void genLabelerDate() {
for (int i = 0; i < 32; i++) {
LabelerDate labelerDate = new LabelerDate();
labelerDate.setNumber(Integer.toString(i));
labelerDates.add(labelerDate);
if (i == 0 || i == 31) {
labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
} else {
labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
}
}
}
//
private void setDateValue() {
int expectedPositionDateColor = Math.round(allPixelsDate / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
// set color here
dateAdapter.setSelecteditem(setColorDate);
}
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private int selectedItem = -1;
public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
this.dateDataList = dateData;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateAdapter.DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
if (viewType == VIEW_TYPE_PADDING) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
}
return new DateViewHolder(view);
}
#Override
public void onBindViewHolder(DateAdapter.DateViewHolder holder, int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(labelerDate.getNumber());
holder.tvDate.setVisibility(View.VISIBLE);
Log.d(TAG, "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.d(TAG, "center" + position);
holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
holder.tvDate.setTextSize(35);
} else {
holder.tvDate.setTextColor(Color.WHITE);
holder.tvDate.setTextSize(18);
}
} else {
holder.tvDate.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return dateDataList.size();
}
#Override
public int getItemViewType(int position) {
LabelerDate labelerDate = dateDataList.get(position);
if (labelerDate.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class DateViewHolder extends RecyclerView.ViewHolder {
public TextView tvDate;
public DateViewHolder(View itemView) {
super(itemView);
tvDate = (TextView) itemView.findViewById(R.id.txt_date);
}
}
}
private class LabelerDate {
private int type;
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
}
You can use a LinearSnapHelper
Attach to your recyclerView like
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(this)
Then, to get the center view, use
snapHelper.findSnapView(horizontalScrollView.layoutManager)?
As mentioned in the other answer, there is no direct way to do this.
This is probably how you can achieve what you described in the question.
Know the number of items visible on the screen.
Select the middle item programmatically every time the view is scrolled.
Keep a partially transparent image as an overlay on the middle item on the recyclerview. (You'll need to compute the coordinates based on the width of the recycler view or width of the screen and the width of the overlay image you choose to put.
Refresh the selected value in a text view below the recycler view every time there is a scroll.
The image overlays have to be placed in a way they appear connected and as one single control.
For this feature use EcoGallery library:
https://github.com/falnatsheh/EcoGallery
At first, I needed something similar, not this. But I was able to adapt #TranHieu solution to my needs, so I voted up his solution.
I wanted to create full-screen horizontal recyclerview that after user sroll updates scrollPosition to mostVisibleItem.
setup:
private void setUpScrolling() {
mRecyclerVIew.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mRecyclerVIew.getViewTreeObserver().removeOnPreDrawListener(this);
CustomScrollListener listener = (CustomScrollListener) mScrollListener;
listener.width = mRecyclerVIew.getMeasuredWidth();
listener.dx = 0;
return true;
}
});
}
listener:
private class CustomScrollListener extends OnScrollListener {
private int mLastDx = 0;
int width = 0;
int dx = 0;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mLastDx != dx) {
scrollToMostVisibleItem();
} else {
dx = 0;
mLastDx = 0;
}
}
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
this.dx += dx;
}
private void scrollToMostVisibleItem() {
int direction = (dx > 0) ? 1 : -1;
dx = Math.abs(dx);
int shiftCount = Math.round(dx / width);
int pixelShift = dx % width;
if (pixelShift > width / 2) {
shiftCount++;
}
float targetScrollPixels = shiftCount * width;
float finalScrollPixels = (targetScrollPixels - dx) * direction;
if (finalScrollPixels != 0) {
mRecyclerVIew.smoothScrollBy((int) finalScrollPixels, 0);
mLastDx = (int) finalScrollPixels;
dx = 0;
}
}
}
I used another approach in my case.
you can find the deatils here: RecyclerView - How highlight central visible item during scroll1
In my opinion, my solution is more easy than the others.
If someone is looking for a more generic implementation, here is my code based on the answers of this thread:
Add the CenterLinearSnapHelper
public class CenterLinearSnapHelper extends LinearSnapHelper {
//Constants
public static final String TAG = CenterLinearSnapHelper.class.getSimpleName();
//Attributes
private Context context;
private float itemWidth;
private OnPaddingComputationListener listener;
//Constructors
/**
* A linear snap helper which helps centering the items in a recyclerview.
*
* #param itemWidth The (fixed) width of a child view in pixels.
*/
public CenterLinearSnapHelper(float itemWidth) {
this.itemWidth = itemWidth;
}
public void attachToRecyclerView(#Nullable RecyclerView recyclerView,
#NonNull OnPaddingComputationListener listener) throws IllegalStateException {
this.listener = listener;
//Calculates the padding for the first and end item
calculatePadding(recyclerView);
//Create a LinearSnapHelper and attach the recyclerView to it.
attachToRecyclerView(recyclerView);
}
public float getItemWidth() {
return itemWidth;
}
private void calculatePadding(RecyclerView recyclerView) {
if (recyclerView == null)
return;
ViewTreeObserver observer = recyclerView.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
int finalWidth = recyclerView.getMeasuredWidth();
float padding = (finalWidth - itemWidth) / 2;
listener.onPadding(padding, finalWidth);
return true;
}
});
}
public interface OnPaddingComputationListener {
void onPadding(float padding, int finalWidth);
}
}
In your Activity/Fragment where you create your RecyclerView:
float itemWidth = getResources().getDimension(R.dimen.favorite_room_width);
CenterLinearSnapHelper snapHelper = new CenterLinearSnapHelper(itemWidth);
snapHelper.attachToRecyclerView(binding.listFavorites, (padding, finalWidth) -> {
//Set the adapter
roomAdapter = new RoomAdapter(requireContext(), rooms);
roomAdapter.addPaddingItems((int) padding);
roomAdapter.setOnToggleClickListener(FavoritesFragment.this);
binding.listFavorites.setAdapter(roomAdapter);
});
In your adapter:
public void addPaddingItems(int padding) {
if (padding < 0)
throw new IllegalStateException("Padding cannot be smaller than 0");
this.padding = padding;
//Add 2 new items as the first and last
//NOTE: If you update your existing dataset (e.g add new items), you should redo the calculation!
rooms.add(0, new Room("First"));
rooms.add(rooms.size(), new Room("Last"));
}
#Override
public int getItemViewType(int position) {
if (padding >= 0 && (position == 0 || position == rooms.size() - 1)) {
return VIEW_TYPE_PADDING;
}
return VIEW_TYPE_ITEM;
}
#NonNull
#Override
public RoomViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ViewFavoriteRoomBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_favorite_room, parent, false);
if (viewType == VIEW_TYPE_PADDING) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) binding.getRoot().getLayoutParams();
layoutParams.width = padding;
binding.getRoot().setLayoutParams(layoutParams);
}
RoomViewHolder viewHolder = new RoomViewHolder(context, binding, onToggleClickListener);
viewHolder.getRecyclerView().setRecycledViewPool(viewPool);
return viewHolder;
}

Categories

Resources