Could you help me to find a proper way without leak to animate a RecylcerView's item's sub item?
I want to create a two level drawer menu, where the menu items are in a RecyclerView created by an adapter. Some of the menu items contain sub items, and this items can open with an arrow at the end to add sub items to the list below the parent item.
I want to animate those arrows to rotate 180 degree when the adapter detects parent item have to open/close and show/remove sub items.
The whole thing is working but I'm not happy with the solution of the animation. :(
What the tricky part is that I'm using databinding to fill those item's with data.
Here's my class what contains the data:
public class MenuItem extends BaseObservable {
public ObservableField<String> name = new ObservableField<>();
public ObservableBoolean selected = new ObservableBoolean(false);
public ObservableBoolean isSubItem = new ObservableBoolean(false);
public MenuItem parent;
public List<MenuItem> subItems = new ArrayList<>(0);
private DrawerAdapter.OnDrawerItemClickListener listener;
/* It's a potencial leak */
public View arrow;
.....
public void animateArrow(boolean isOpen) {
if(arrow == null)
return;
float degrees = isOpen ? 180f : 0f;
arrow.animate().roatate(degrees).setDuration(300).start();
}
}
Here's my ViewHolder:
public class DrawerItemHolder extends RecyclerView.ViewHolder {
public DrawerItemBinder ui;
public DrawerItemHolder(View itemView) {
super(itemView);
ui = DataBindingUtil.bind(itemView);
}
}
Here's how I bind to ViewHolder:
#Override
public void onBindViewHolder(DrawerItemHolder holder, int position) {
MenuItem item = items.get(position);
if(!item.subItems.isEmpty())
item.arrow = holder.ui.arrow;
holder.ui.setData(item);
}
I attach the arrow view only those items what has sub item.
And when the adapter decides to open an item it calls this method:
private void openCategories(MenuItem item) {
...
/* Animate to close state the already opened category's arrow */
if(openedCategory != null)
openedCategory.animateOpener(false);
openedCategory = item;
/* Animate to opened state the newly opened category */
openedCategory.animateOpener(true);
notifyItemRangeInserted(...);
}
The openedCategory is also a MenuItem, a reference to the parent item what is actually open.
And when it want to close an item calls this:
private void closeCategories() {
if (openedCategory == null)
return;
...
notifyItemRangeRemoved(...);
/* Animate to closed state the already opened category's arrow */
openedCategory.animateOpener(false);
openedCategory = null;
}
So my problem is this. How can I animate a subview of parent items without leaking those arrow views by holding reference about them in the MenuItem class?
Best Regards!
Related
I am actually making some visibility changes to items that are clicked of the recycler view. But when the user clicks on one object and then clicks on the other object then the previous object should come to its initial state.
The manager.findViewByPosition(position) is working fine if the view is in focus of the screen but I am not able to get the view if the element is not in current focus.
For example:- the user clicks on 1st(position) item then clicks on the last position then the findViewByPosition returns a null.
Please help and let me know if there is some other way of doing it.
The expected result should be the view of the last item to be refreshed but it's not happening for the views that are not in the current focus of the screen.
Below is my code snippet. Updated with what you suggested.
public class BodyPartWithMmtRecyclerView extends
RecyclerView.Adapter<BodyPartWithMmtRecyclerView.ViewHolder>
{
//variables defined.
int selectedPosition = -1;
static class ViewHolder extends RecyclerView.ViewHolder {
//All the view items declared here.
ViewHolder(View view) {
super(view);
//All the views are defined here.
}
}
public BodyPartWithMmtRecyclerView(List<BodyPartWithMmtSelectionModel> bodyPartsList, Context context){
//array list initialization and shared preference variables initialization
}
public BodyPartWithMmtRecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//Creating a new view.
}
public void onBindViewHolder(#NonNull final BodyPartWithMmtRecyclerView.ViewHolder holder, #SuppressLint("RecyclerView") final int position) {
BodyPartWithMmtSelectionModel bodyPartWithMmtSelectionModel = bodyPartsList.get(position);
holder.iv_bodypart.setImageResource(bodyPartWithMmtSelectionModel.getIv_body_part());
holder.tv_body_part_name.setText(bodyPartWithMmtSelectionModel.getExercise_name());
if(selectedPosition!=position && selectedPosition!=-1){
//updated the elements view to default view. Like made the visibility and other changes here.
}
//some click listeners on the sub-elements of the items. Like textviews, spinner, etc
holder.iv_bodypart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((BodyPartSelection)context).setFabVisible();
if(selectedPosition!=-1){
((BodyPartSelection)context).visibilityChanged(selectedPosition,position);
/*here what I was doing is whenever the user clicks on an item I check weather a previous item is clicked or not then if yes then I send the position to a function that makes it to default but the issue was that if the item is not in the focus of the screen the findViewByPosition returns null.*/
}
selectedPosition = position;
bodypartSelected = holder.tv_body_part_name.getText().toString();
holder.iv_bodypart.setVisibility(View.INVISIBLE);
holder.rl_left_right.setVisibility(View.VISIBLE);
}
});
//and other listeners below
}
#Override
public int getItemCount() {
return bodyPartsList==null?0:bodyPartsList.size();
}
#Override
public int getItemViewType(int position) {
return position;
}
}
VisibilityChanged function
public void visibilityChanged(int position, int clicked){
View view = manager.findViewByPosition(position);
if(view!=null) {
Log.i("inside","visibility change");
ImageView imageView = view.findViewById(R.id.bodypartImage);
//other elements and changing the visibility of elemets to default.
}
}
I have updated my code based on the snippet you updated. Please don't change the visibility condition if-else I have added with any different logic which I saw in your code snippet. As you did, it will not update both selected and default view as RecyclerView reuse the view layout. So if the condition is not proper, you may see multiple items as selected or some other types of unwated behaviour.
public void onBindViewHolder(#NonNull final BodyPartWithMmtRecyclerView.ViewHolder holder, #SuppressLint("RecyclerView") final int position) {
BodyPartWithMmtSelectionModel bodyPartWithMmtSelectionModel = bodyPartsList.get(position);
holder.iv_bodypart.setImageResource(bodyPartWithMmtSelectionModel.getIv_body_part());
holder.tv_body_part_name.setText(bodyPartWithMmtSelectionModel.getExercise_name());
if(selectedPosition == position){
//updated the elements view to SELECTED VIEW. Like made the visibility and other changes here.
} else {
//updated the elements view to default view. Like made the visibility and other changes here.
}
//some click listeners on the sub-elements of the items. Like textviews, spinner, etc
holder.iv_bodypart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((BodyPartSelection)context).setFabVisible();
/Comment by Hari: Don't try to change the visibility of default as it will be done automatically after calling notifyDataSetChanged(). */
if(selectedPosition!=-1){
((BodyPartSelection)context).visibilityChanged(selectedPosition,position);
/*here what I was doing is whenever the user clicks on an item I check weather a previous item is clicked or not then if yes then I send the position to a function that makes it to default but the issue was that if the item is not in the focus of the screen the findViewByPosition returns null.*/
/*Comment by Hari: This snippet is valuable which is missing as you are getting null issue here.
However Don't try to change the visibility of default as it will be done automatically after calling notifyDataSetChanged(). */
}
selectedPosition = position;
bodypartSelected = holder.tv_body_part_name.getText().toString();
holder.iv_bodypart.setVisibility(View.INVISIBLE);
holder.rl_left_right.setVisibility(View.VISIBLE);
//Keep this as last statement in onClick
notifyDataSetChanged();
}
});
//and other listeners below
}
Let me know your further response.
Based on #Hari N Jha's Answer.
Call notifyDataSetChanged() when you update anything. E.g
int selectedPosition = -1;
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
//....
if(position == selectedPosition) {
//Add background color change of your layout or as you want for selected item.
} else {
//Add background color change of your layout or as you want for default item.
}
notifyDataSetChanged(); //Call notifyDataSetChanged() here after done all the stufs
//...
}
Generally
I want to control the ViewHolder inflated Views of my RecyclerView from outside of the ViewHolder and the RecyclerView classes. In other words, I want to have control of these views from other methods/classes.
My case (en example)
In my specific case, I made a photo gallery activity which allows the user to perform selection and deselection of each inflated view, notifying which items are selected by highlighting them.
For now, the user is able to do that by clicking each generated object / View; then, actions on specific child of RecyclerView / adapter are possible thanks to "setOnClickListener" and "setOnLongClickListener" methods, which perform the corresponding actions in methods inside the ViewHolder class.
But when activity is restarted (i.e. for device rotation) the selection goes lost and the user should perform the selection again (i.e. for deleting photos).
Assuming that positions of the selected photos are kept (for example via bundle, or via an array) is possible to restore selection (i.e. highlighting the corresponding item / views) on the adapter views after that the activity is re-started? If yes, how?
Some code
The code below contains the Recyclerview class and the AdapterView class, which both are child of an activity Class.
private class ImageGalleryAdapter extends RecyclerView.Adapter<ImageGalleryAdapter.MyViewHolder> {
private ArrayList<PhotoObject.PhotoElement> photoAL;
private Context mContext;
public ImageGalleryAdapter(Context context, ArrayList<PhotoObject.PhotoElement> photosToPreviewInGallery) {
mContext = context;
photoAL = photosToPreviewInGallery;
}
#Override
public ImageGalleryAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
// Inflate the layout
View itemView = inflater.inflate(R.layout.item_photo, parent, false);
ImageGalleryAdapter.MyViewHolder viewHolder = new ImageGalleryAdapter.MyViewHolder(itemView);
// Retrieving the itemView
return viewHolder;
}
#Override
public void onBindViewHolder(ImageGalleryAdapter.MyViewHolder holder, int position) {
PhotoObject.PhotoElement previewPhotoInGallery = photoAL.get(position);
ImageView imageView = holder.mPhotoImageView;
GlideApp.with(mContext)
.load(previewPhotoInGallery.getUrl())
.placeholder(R.drawable.ic_cloud_off_red)
.into(imageView);
}
//The method which gives back the number of items to load as photo.
#Override
public int getItemCount() {
return (photoAL.size());
}
// The class that assigns a view holder for each Image and checkbox in the RecyclerView.
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public ImageView mPhotoImageView;
public CheckBox mPhotoCheckBox;
public MyViewHolder(View item_view) {
super(item_view);
mPhotoImageView = (ImageView) item_view.findViewById(R.id.item_photo_iv);
mPhotoCheckBox = (CheckBox) item_view.findViewById(R.id.item_photo_checkbox);
item_view.setOnClickListener(this);
item_view.setOnLongClickListener(this);
// Retrieving the item_view
}
// The method for managing the click on an image.
#Override
public void onClick(View view) {
itemSelection(view);
}
// Manages the selection of the items.
private void itemSelection(View item) {
// Retrieving the item
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
if (!item.isSelected()) {
// Add clicked item to the selected ones
MultiPhotoShootingActivity.manageSelection(true, position);
// Visually highlighting the ImageView
item.setSelected(true);
mPhotoCheckBox.setChecked(true);
mPhotoCheckBox.setVisibility(View.VISIBLE);
} else {
// Remove clicked item from the selected ones
MultiPhotoShootingActivity.manageSelection(false, position);
// Removing the visual highlights on the ImageView
item.setSelected(false);
mPhotoCheckBox.setChecked(false);
mPhotoCheckBox.setVisibility(View.INVISIBLE);
}
}
}
// The method for managing the long click on an image.
#Override
public boolean onLongClick(View view) {
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION) {
Intent intent = new Intent(mContext, PhotoDetail.class);
intent.putExtra("KEY4URL", activityPhotoObject.getPath(position));
startActivity(intent);
}
// return true to indicate that the click was handled (if you return false onClick will be triggered too)
return true;
}
}
}
Thank you for your time.
You shouldn't "control" views from outside the adapter. Instead, Override onSaveState and onRestoreState in your activity. Make same methods in your adapter with passing the bundle to the adapter in order to save state. save an integer array of positions that were selected into the bundle(that you passed into an adapter). In corresponding way, you can get the array of selected positions from the bundle of On restore state.
activity:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
adapter.onRestoreInstanceState(savedInstanceState);
}
in your adapter:
public void onRestoreInstanceState(Bundle state){
selectedItemsArray = state.getIntArray("my_array_key")
}
#Alessandro
You can handle the Runtime changes by yourself.
In your manifest, you can define the changes that your activity will handle by itself and it will not be restarted.
android:configChanges="orientation|keyboardHidden"
After that, you'll have to handle the Configuration changes that you declared in your manifest using this method in your activity:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// Do your thing
}
}
SOLVED
Find out that for solving the problem I had to accomplish two little tasks:
saving and restoring the selected item selection state (for example via an array, as helpfully suggested by #Inkognito);
retrieving the views for applying the selection, based on the position inside the RecyclerView.
So, I had to modify some code.
Before proceeding, I would like to point out that the Activity class has a sub-class, which is the Adapter class (named ImageGalleryAdapter); the Adapter subclass, in turn, has its own subclass, which is the ViewHolder class (named MyViewHolder).
So: Activity class -> Adapter class -> ViewHolder class
Code modified in the parent class (the activity class, in which the RecyclerView is)
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
adapter.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
adapter.onRestoreInstanceState(savedInstanceState);
}
In the onSaveInstanceState and onRestoreInstanceState methods, I added the references for saving and restoring instance states of the "adapter" sub-class.
Code added in the adapter class (which is inside the RecyclerView class)
private boolean [] selectedItemsArray;
private void onSaveInstanceState(Bundle outState) {
outState.putBooleanArray("my_array_key" , selectedItemsArray = mpsaPO.getItemsSelected());
}
private void onRestoreInstanceState(Bundle state) {
if (state != null) {
selectedItemsArray = state.getBooleanArray("my_array_key");
}
}
The selectedItemsArray is a boolean array in which the information of which elements of the RecyclerView are selected (true = selected; false = not selected) is contained.
Then, adding this element in the saved instance and retrieved via the activity class, makes the app able to know which are the views selected after that the activity is re-created.
Code added inside the onBindViewHolder method, which is inside the adapter class
if (selectedItemsArray != null) {
if (selectedItemsArray[position]) {
holder.itemView.setSelected(true);
holder.mPhotoCheckBox.setChecked(true);
holder.mPhotoCheckBox.setVisibility(View.VISIBLE);
}
}
With this last part of code, we are applying the selection to the corresponding views based on which items/views were selected before that the activity was saved.
The holer object contains the itemView and mPhotoCheckBox objectsm on which we can perform the selection.
This is a difficult one, so I attached a picture to help visualize a bit.
So I have a recyclerview which contains a palette of colors. It loads the colors by creating a new ImageView for each color, and then changing the color and drawable of each imageview, having a different one for the "selected color". The selected color is also used to change the color of the toolbar from the parent DialogFragment.
The problem comes that the OnClicklistener hasn't worked at all after hours of research. Right now, I can't use an OnClickListener without the app crashing.
Now, lets get to the actual code and problems.
I'll declare (to you) some variables and classes I'm using:
ArrayList<String> colorsList = //arraylist which contains all the colors which can be displayed
NewNotebookFragment = //DialogFragment, contains a toolbar, miscellaneous views and a rvNewNotebook
RecyclerView rvNewNotebook = //the recyclerview containing imageviews representing each individual color retrieved from colorsList
NewNotebookAdapter = adapter which is bound to rvNewNotebook
Right now, what I've done is make a method in NewNotebookFragment called changeColor which can receive an int, representing the position of the color which was selected. changeColor() changes the color of the toolbar. changeColor() is called from an onClick method from an OnclickListener, defined in the Viewholder in NewNotebookAdapter.
Now comes the actual code.
newNotebookFragment:
public class NewNotebookFragment extends DialogFragment {
RecyclerView rvNewNotebook;
int activeColor;
ArrayList<String> colors=Helpers.getPossibleColors();
#Override
public View onCreateView ...}
#Override
public void onViewCreated(View v, Bundle savedInstanceState){
activeColor=new Random().nextInt(colors.size());
toolbar = (Toolbar) v.findViewById(R.id.newnotebooktoolbar);
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
return false;
}
});
toolbar.inflateMenu(R.menu.newnotebook);
toolbar.setTitle("Create new Notebook");
if(Helpers.isColorDark(Color.parseColor(colors.get(activeColor)))){
toolbar.setTitleTextColor(getResources().getColor(R.color.md_dark_primary_text));
}else{
toolbar.setTitleTextColor(getResources().getColor(R.color.md_light_primary_text));
}
rvNewNotebook = (RecyclerView) v.findViewById(R.id.rvNewNotebook);
final GridLayoutManager rvNotebookManager = new GridLayoutManager(getContext(),6);
rvNewNotebook.setLayoutManager(rvNotebookManager);
NewNotebookAdapter adapter= new NewNotebookAdapter(getContext(), Helpers.getPossibleColors(), activeColor);
toolbar.setBackgroundColor(Color.parseColor(Helpers.getPossibleColors().get(activeColor)));
rvNewNotebook.setAdapter(adapter);
Log.d("onViewCreated", ""+rvNewNotebook.getWidth());
}
#Override public void onResume(){...}
public static NewNotebookFragment newInstance() {...}
public void changeColor(int position){
if(Helpers.isColorDark(Color.parseColor(colors.get(position)))){
toolbar.setTitleTextColor(getResources().getColor(R.color.md_dark_primary_text));
}else{
toolbar.setTitleTextColor(getResources().getColor(R.color.md_light_primary_text));
}
toolbar.setBackgroundColor(Color.parseColor(Helpers.getPossibleColors().get(position)));
}
NewNotebookAdapter:
public class NewNotebookAdapter extends RecyclerView.Adapter<NewNotebookAdapter.ViewHolder>{
Context context;
ArrayList<String> colors = new ArrayList<>();
int activeColor;
public NewNotebookAdapter(){
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public ImageView ivSwatch;
public Toolbar toolbar;
public ViewHolder(View itemView) {
super(itemView);
ivSwatch = (ImageView) itemView.findViewById(R.id.ivSwatch);
toolbar = (Toolbar) itemView.findViewById(R.id.newnotebooktoolbar);
ivSwatch.setOnClickListener(this);
}
#Override
public void onClick(View view) {
NewNotebookFragment newNotebookFragment = new NewNotebookFragment();
final int adapterPosition = ViewHolder.this.getAdapterPosition();
Log.d("OnClick. Adapter", "getAdapterPosition: "+adapterPosition);
newNotebookFragment.changeColor(adapterPosition);
}
}
public NewNotebookAdapter(Context context, ArrayList<String> colors, int activeColor){
this.context=context;
this.colors=colors;
this.activeColor=activeColor;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.colorcircle,parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Drawable drawablefull = ContextCompat.getDrawable(context,R.drawable.colorcircle);
Drawable drawableHollow = ContextCompat.getDrawable(context,R.drawable.coloroutlinecircle);
if (position==activeColor){
holder.ivSwatch.setImageDrawable(drawablefull.mutate());
}else if (position!=activeColor){
holder.ivSwatch.setImageDrawable(drawableHollow.mutate());
}
holder.ivSwatch.setColorFilter(Color.parseColor(colors.get(position)));
The current problem I have is the following stack trace:
FATAL EXCEPTION: main
Process: com.twotowerstudios.virtualnotebookdesign, PID: 32272
java.lang.IllegalStateException: Fragment NewNotebookFragment{b10003e} not attached to Activity
at android.support.v4.app.Fragment.getResources(Fragment.java:648)
at com.twotowerstudios.virtualnotebookdesign.NewNotebookDialog.NewNotebookFragment.changeColor(NewNotebookFragment.java:85)
at com.twotowerstudios.virtualnotebookdesign.NewNotebookDialog.NewNotebookAdapter$ViewHolder.onClick(NewNotebookAdapter.java:49)
at android.view.View.performClick(View.java)
at android.view.View$PerformClick.run(View.java)
at android.os.Handler.handleCallback(Handler.java)
at android.os.Handler.dispatchMessage(Handler.java)
at android.os.Looper.loop(Looper.java)
at android.app.ActivityThread.main(ActivityThread.java)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
Now I'm almost positive this is related to my onClick method, which creates a new instance of NewNotebookFragment, causing the fragment I'm using in the onClick method to not actually be the same as the one which is already being used and displayed. HOWEVER, I'm making this thread because I have no real idea on how to solve the issue as a whole. I don't know how I could for example get the actual NewNotebookFragment from the onClick method so I could modify it or call methods from it. I don't know if once that
s fixed if it would even work or anything. How could I get this working?
Edit: I got it working perfectly. Basically what I did was define the setonclickListener inside the onBindViewHolder, on the Imageviews. The clickListener will call an interface called AdapterInterface, and use a method in it called clickListener, which allows me to pass the int of the color to the parent fragment. The fragment will implement that interface defined in the adapter, and it will override clickListener, using the color passed to it and from it invoking the changeColor method. If anyone in the future needs examples, feel free to contact me. The relevant changes are in these two files, uploaded to Github: https://gist.github.com/coldblade2000/0c2cac8b1af4df5985fe6cebebc9cff2 The links contain a certain level of explanation and documentation about the two classes
Looking at your code, you're creating a new Fragment every time it's clicked. I believe what you want is to change color of the dialog fragment toolbar. The first thing you can do is to create an interface listener for as a Callback to Fragment, which is passed into Adapter. Something like this
public interface ViewHolderOnClickListener {
//This method can be any parameters, I'm pasing color here since you need the color code
public void onViewHolderClick(View itemView, int position, int color);
}
And in your adapter, and viewholder Constructor methods, add an interface instance as your parameter. You can either directly pass it on creating Adapter or make your Fragment implements the interface and pass it with "this"
public TempAdapter(ViewHolderOnClickListener viewHolderOnClickListener) {
this.viewHolderOnClickListener = viewHolderOnClickListener;
}
And In your ViewHolder, do a callback with
#Override
public void onClick(View view) {
viewHolderOnClickListener.onViewHolderClick(*PARAMS HERE*);
}
That way, your fragment can listen to OnClick That is happening inside your ViewHolder, and respond accordingly.
Another way is to use EventBuses
I'm using a RecyclerView to implement a NavigationDrawer.
I got click events working, but I can't figure out how to have the first item selected on App start and following that keep the selected item higlighted even if the drawer is not shown.
All I've been able to find is multi-selection in RecyclerView.
I actually just implemented this in an app I am working on. So this method worked:
First create a variable to track the current selected position at the top of your adapter:
private int selectedItem;
Then in your Adapter constructor initiate the selectedItem value you would like:
public NavDrawerMenuListAdapter(Context context, List<NavDrawerItem> data, NavDrawerMenuListViewHolder.NavDrawerMenuClickInterface listener) {
this.context = context;
mLayoutInflater = LayoutInflater.from(context);
this.navDrawerItems = data;
this.listener = listener;
selectedItem = 0;
}
Here I use 0 as this is the first item in my menu.
Then in your onBindViewHolder(NavDrawerMenuListViewHolder holder, int position) just check whether your selectedItem == position and set the background of some view to a seleted background like so:
if (selectedItem == position) {
holder.single_title_textview.setTextColor(0xff86872b);
holder.nav_drawer_item_holder.setBackgroundColor(Color.DKGRAY);
}
Here I set the text color to green and give the Realativelayout parent a gray background on start. You can, of course, customize this in any way you like.
To implement a selection of an item and keep the state I use the following method:
public void selectTaskListItem(int pos) {
int previousItem = selectedItem;
selectedItem = pos;
notifyItemChanged(previousItem);
notifyItemChanged(pos);
}
This method I usually call from the OnClick() method.
Hope this helps!.
I am working on an android application, I have a list view with 10 items. I need to do the following. When the user clicks a button, I want the list to smooth scroll to item at position 5, so this item is displayed on the top of the list.
I have found 2 methods that can be used for this, but both methods are not working exactly how I need:
listView.setSelection(5) this will scroll to the row and put it on top of the list But without animation
list.smoothScrollToPosition(5) this will scroll the listview untill the row is visible but it will not put it on top (it is at the bottom of the page) and if the row is allready visible it will not scroll as it considers it is visible.
So is there a way to have the same behavior as the setSelection method but with smoothscrolling?
Thank you
I believe smoothScrollToPositionFromTop() does what you want.
There's also one that will take the desired animation duration in milliseconds as an argument.
So is there a way to have the same behavior as the setSelection method
but with smoothscrolling?
You could post a delayed Runnable and create your own smooth scroll effect using ListView.setSelection. Here's an example:
private ListView mListView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mListView.post(new PositionScroller(this));
}
private static final class PositionScroller implements Runnable {
private static final int SMOOTH_SCROLL_DURATION = 25;
private int mSelectedPosition;
private final WeakReference<YourParentActivity> mParent;
private PositionScroller(YourParentActivity parent) {
mParent = new WeakReference<YourParentActivity>(parent);
}
#Override
public void run() {
final ListView list = mParent.get().mListView;
if (mSelectedPosition <= 5) {
if (list.postDelayed(this, SMOOTH_SCROLL_DURATION)) {
list.setSelection(mSelectedPosition++);
}
}
}
}