Showing/hiding items - listview - android

I have a special question which i havent't found the answer. I can't deal with this problem.
So... is there any way to hide or show items(images) dynamically on ListView?
I mean, after tap on toolbar menu item called "Edit", on ListView next to the texts should appear images. When user click on this image, this text should be removed from ListView and database. And, after tap on "Done", images that have previously appeared should gone.
I spent all night for this and I haven't found the answer, so guys please help me!
I have this:
And want this after click on Edit:
That's part of my Adapter (extends from BaseAdapter)
public View getView(final int position, View convertView, final ViewGroup parent) {
if(convertView == null){
convertView = inflater.inflate(R.layout.list, null);
TextView textView = (TextView) convertView.findViewById(R.id.textView);
textView.setText(todo.get(position));
ImageView circleImage = (ImageView) convertView.findViewById(R.id.circleImage);
Picasso.with(mActivity).load(R.drawable.circle_image).transform(new CircleTransform()).into(circleImage);
}
if(editMode){
circleImage.setVisibility(View.GONE);
} else circleImage.setVisibility(View.VISIBLE);
return convertView;
}
public void setMode(boolean editMode){
this.editMode=editMode;
notifyDataSetChanged();
}
onOptionItemSelected from MainActivity:
else if(id == R.id.edit){
mToDoFragment = new ToDOFragment();
mFToDoFragment.setEditable(true);
invalidateOptionsMenu();
return true;
}
And, last part from ToDoFragment:
public void setEditable(boolean editable){
mFavouritesListAdapter.setMode(editable);
}
After that, I'm getting such beutiful NullPointerException :(
java.lang.NullPointerException: Attempt to invoke virtual method 'void adapters.ToDoListAdapter.setMode(boolean)' on a null object reference

Hi please follow the blow steps. I hope it will help you.
First you have flag that indicate that current mode for example the list view in edit mode or in normal mode (view mode)
After define the mode. Take the array list with your model or object. bind you array list with BaseAdapter. I guess you are using the ListView. If you are using the RecyclerView then the process or logic will remain the same.
In inside the getView(....). Define you layout using LayoutInflater
After checking the convert-view is not null and inflating the layout.
Put the logic for check the flag of mode.
Check whether the mode is edit mode or normal view mode. If edit then show the [x] icon for each row item otherwise hide/gone the image view.
If the mode is edit when click on [x] image view remove position of the model or object from the dataset that is bind with the BaseAdapter. And call notifyDatasetChanged(). This will refresh the list view and remove the item from listview.
If edit mode is complete and user click on "Done" Option from menu item. Then simply change the flag of mode from edit to done mode. call again notifyDatasetChanged() method of your BaseAdapter
That's it. You done let me know if you have any query.

Related

How to show and hide view in recycler view in Android?

In my app I am using recycler view.I want to show and hide view on particular condition.But when I scroll recycler views I am not getting expected behaviour.When I Visible a view it gets visible for other rows as well randomly.
What I understand is when it recycles it reuses view and when previous view when it gets recycled it finds the visibility of that view.How can I hide view on particular condition? here is my adapter code
#Override
public void onBindViewHolder(UrduRhymesViewHolder holder, int position) {
RhymesModel current = mUrduRhymesList.get(position);
AppUtility.setCustomFont(mContext, holder.tvUrduRhymesName, Constants.HANDLEE_REGULAR);
holder.tvUrduRhymesName.setText(current.getRhymeName());
holder.ivUrduRhymesLogo.setImageUrl(current.getThumbnailUrl(), mImageRequest);
int status = AppUtility.getFavouriteStatus(mContext, current.getRhymeName(), new UrduRhymesDb(mContext));
if (status == 0)
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star_unactive);
else
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star);
ProgressbarDetails progressbarDetails = ProgressbarDetails.getProgressDetail(current.getRhymeName());
if (progressbarDetails == null) {
progressbarDetails = new ProgressbarDetails();
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
} else {
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
}
ProgressbarDetails.addUpdateProgressDetail(current.getRhymeName(), progressbarDetails);
if (progressbarDetails != null && progressbarDetails.isDownloading) {
Log.e("test","downloading foe position "+position );
holder.downloadButtonLayout.setBackgroundResource(R.mipmap.btn_download);
holder.pbRhymeDownload.setVisibility(View.VISIBLE);
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
} else {
Log.e("test","should not be visible for position "+position);
holder.pbRhymeDownload.setVisibility(View.GONE);
}
}
Here progressbarDetails.isDownloading (having value true) is the criteria when I want to show my view but is else clause it is not hiding my view
Edit: Here ProgressbarDetails (Singleton )is a class keeping reference of every row of adapter's progress bar.
No direct way of hiding and unhiding recylerview childitems.
Solution:
Let us assume that the recyclerview adapter is ArrayList
Now make another arraylist (temp_list)
Scenarios:
Hide: iterate through your adapter items and remove the ones that you want to hide. Put each of these into temp_list. After iteration is over, call notifyDataSetChanged()
Show: iterate through your temp_list items and remove the ones that you want to show. Put each of these into adapter. After iteration is over, call notifyDataSetChanged()
You should add a flag in your viewHolder that indicates if this view should be displayed or not . and check this flag every time in the onBindViewHolder.
because the recyclerView reuses the same views you should make a decision depending on something special for every view in you viewHolder.
do u mean when your data has been changed?
and your layout want to change ?
adapter.notifyDataSetChanged();

Items ImageButton drawable change upon click in ListView seems to change for 2 items at a time

I know this question has been asked before and got an understanding roughly of whats happening but i cant seem to find a solution.
In my Custom List Adapter and inside public View getView(int position, View convertView, ViewGroup parent) { ive setup a click function for the items ImageButton.
final ImageButton bookmark = (ImageButton)
convertView.findViewById(R.id.bookmarkthis);
bookmark.setTag(position);
bookmark.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bookmark.setImageResource(R.drawable.ic_bookmarked);
bookmark.setTag(R.drawable.ic_bookmarked);
}
});
The Imagebutton is a clear star and when Clicked it changes that same drawable to a yellow star. It works Ok.
Problem is when i click Item in Position 0 Star Icon to make yellow , it also changes for item 8 further down the list not in View Yet. if i click position 1 changes also for position 9 and so on.
I had a look around and researched the issue and even tried a holder for the Imagebutton but no go. Something is preventing the drawable change for the ImageButton to its correct Position only.
Thanks
Solution is to initially Set a flag item in the array for each Item. Then onClick set the flag to true just for that item. Then in the getView its just a case of an if statement to check the flag as the Items get Cycled.
//in array creation
items.setFlag("false");
//in getView as you set Text and what ever get the Flag state
String flag = m.getFlag();
//check the flag state and take action in this case change the icon accordingly
if (Objects.equals(flag, "true")) {
bookmark.setImageResource(R.drawable.ic_bookmarked);
bookmark.setTag(R.drawable.ic_bookmarked);
}
else {
bookmark.setImageResource(R.drawable.ic_bookmark);
}
// and in the click function
#Override
public void onClick(View v) {
bookmark.setImageResource(R.drawable.ic_bookmarked);
m.setFlag("true");
}
You need to reset your bookmark image resource & tag values a non-null convertView is passed in to getView. Your onClick handler is setting them to these values:
bookmark.setImageResource(R.drawable.ic_bookmarked);
bookmark.setTag(R.drawable.ic_bookmarked);
So you'll need to reset them to the default values when showing a new list item.
ListView reuses the views that fall out of range. You don't reset the "bookmark" icon when reusing the views (when you get a non-null convertView). Make sure to always reset all properties of your views to the correct values, and you won't have a problem.

How to reload activity from onClickListener in ArrayAdapter

I'm working on a fragment that contains a listview (this fragment is PlaceHolderFragment generated when create activity). I extends ArrayAdapter to make my custom adapter and fill my listview with this adapter.
One important thing is in one row of listview, there are 2 buttons: first is the enable/disable button to change status of an user (when user's status is active then it's disable, otherwise enable), second is the delete button (to delete user). So I have to implement OnClickListener for this 2 buttons in method getView() of adapter
When click either buttons, it will send request to server and manipulate database (change user's status or delete user from database). The thing is when I click enable button (for example), it is success and user's status in database is changed, or when I click delete button, user will be delete from database successfully
BUT after I click that button, it's state does not changed (I mean if user is enabled, now the button must change to disable, or if user is deleted, that row must be remove from screen). I have to reload this fragment by hand (switch to other fragment and then come back)
The question is how can I reload activity (I already implement onResume to load all data to adapter, so if I can make this method onResume of fragment run, it will work as my expectation), or at least how can I reload the listview to update new data?
Note: notifyDataSetChanged() DOES NOT work because the data in adapter actually doesn't change yet, only data on server are changed
Note 2: if you need me to post my code, please comment and I will edit my post, because I think my code is long
Thank you and best regards!
EDIT 1
I've posted my solution in the answer below, it fix the problem but I have to say that this is a very very BAD practice in android. For example, when you want to delete an item with this approach, you may want to put a AlertDialog for user to confirm, but AlertDialog can only show in Activity (or Fragment), it can't be show from Adapter. So instead, you should use some different methods such as ContextMenu or CustomDialog.
After couple of weeks searching google and try different methods, I finally found a way to archive what I want, and it's very simple. I post my answer here for anyone facing this problem like me in the future
public class CustomAdapter extends ArrayAdapter<YourClass> {
private List<YourClass> items;
private CustomAdapter adapter;
private Context context;
private Button button;
private YourClass item;
public CustomAdapter(Context context, List<YourClass> items) {
super(context, R.layout.custom_list_item, items);
this.items = items;
this.context = context;
this.adapter = this; //This is an important line, you need this line to keep track the adapter variable
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = LayoutInflater.from(getContext());
v = li.inflate(R.layout.custom_list_item, null);
}
item = items.get(position);
button = (Button) v.findViewById(R.id.button); //Find the button
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//Do something inside here like update, remove your item...
//But the important is
items.remove(item); //Actually change your list of items here
adapter.notifyDataSetChanged(); //notify for change
}
});
}
return v;
}
That's it, all you need to implement when you need the listview to reload in case you need to implement OnClick inside the adapter. Hope you guy find it easier than me when you face this problem
What I understand from your question that you want to refresh the list data when click on one of the button as you need.
Now You should call notifydataset change rather reloading the activity again.
For this you need to change the array that yu are using for the adapter(Don't change the array object just the Item of the array)
like arraylst.get(index).activ=true etc etc and then call notifiydataset change.

Reset background color in Android ListView

In my QuestionsActivity, I am showing a question and bunch of answers. The question is displayed on TextView and answers are displayed on ListView that is composed of TextViews. There is an ActionButton named "Check Answer" and when it is clicked, it shows the correct answer by changing the background color of the TextView in the ListView.
The background changing code looks like this:
if (allAnswers.get(i).isCorrect())
{
mAnswerList.getChildAt(i).setBackgroundColor
(getResources().getColor(R.color.correct_answer_background));
return;
}
and now there are two Buttons at the footer section of this QuestionsActivity called PreviousQuestionButton and NextQuestionButton and they are basically navigation buttons between questions.
The problem is, when I go to the next question after clicking on "Check Answer" button, the answer background color doesn't go away and remains in the next question answer options. I tried invalidate(), refreshDrawableState() method of ListView but no luck!
This is the method which displays the answers for a given question:
private void showAnswers(int questionLocation)
{
int questionId = mAllQuestions.get(questionLocation).getQuestionId();
List<Answer> answers = mAnswerRepository.getAllByQuestionId(questionId);
mAnswerAdapter.clear();
for (int i = 0; i < answers.size(); i++)
{
mAnswerAdapter.add(mOptionLetters[i] + ". "
+ answers.get(i).getAnswerText());
}
mAnswerAdapter.notifyDataSetChanged();
}
My Question
What I want is that when I click on next or previous buttons, the background color of the correct answer in ListView should disappear so that next and previous question button can show non-selected answer options list to the user. Is there any method which resets ListView to a state which does not have any background applied?
For selected answer option, I am using mAnswerList.clearChoices() in order to unselect but it does not apply for correct answer background color.
Well, to reset the color you can very well hard-reset the adapter by creating a new one. So don't clear and add as that may keep the views in the state they were before. I am not too sure about this since I am not clearing or adding from an adapter, but always creating a new one to fulfill my new needs.
Anyway, another reason why things may not go in the direction you want is that the views may get recycled, since we're talking about a ListView. So if you want to highlight a list item, you should keep in the data model the information about highlight by initializing it to false and if the user selects one set the highlight state to true. I suppose the Answer class has as a minimum the following:
public class Answer {
private String data;
private boolean correct;
public String getData() {
return data;
}
public boolean isCorrect() {
return correct;
}
#Override
public String toString() {
return data;
}
}
So your adapter could look close to this - getView method is the most important to notice (don't forget to set to default background if the answer is incorrect or the adapter should not highlight correct answer):
public class MyAdapter extends ArrayAdapter<Answer> {
private boolean showCorrectAnswer;
private List<Answer> modelAnswers;
public MyAdapter(Context context, List<Answer> answers) {
super(context, android.R.layout.simple_list_item_1, answers);
this.modelAnswers = answers;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if(showCorrectAnswer && modelAnswers.get(position).isCorrect()) {
view.setBackgroundColor(getContext().getResources().getColor(R.color.correct_answer_background));
} else {
view.setBackgroundColor(getContext().getResources().getColor(R.color.default_background));
}
}
public void setShowCorrectAnswer(boolean showCorrectAnswer) {
this.showCorrectAnswer = showCorrectAnswer;
notifyDataSetChanged();
}
}
What you need to do is to keep a reference to this custom adapter and if you need to highlight the correct answer or not simply call setShowCorrectAnswer(true / false);. It will trigger a redraw and in the getView() it will decide what to do based on adapter state and correct answer.
Hope it make sense ... I wrote all this while drinking a beer :)
Basically, I agree with the answer from payeli - you should change the background of selected answer in the "next/previous question" button.
But then there is this question:
"Is there any method which resets ListView to a state which does not have any background applied?"
Answer to this (as far as I know) is: not directly. But there are two workarounds.
If you call notfiyDataSetChange, not all views are completely redrawn. If possible, just the appropriate values are changed. It's mainly for performance reasons. (Imagine having 1000 contacts with images and names dumped and redrawn)
So how can you deal with this? (Other then reseting the background in the onClick method) Since you said these items are answers for a question, I assume you are not concerned about performance because there won't be too many of them.
Then you can simply:
A) Create new instance of Adapter instead of changing data in the old one. When you switch adapters, all views in list are removed, so, no recycling can happen in the new adapter.
B) Create custom Adapter, override getView method and make sure every time view is requested, you return a new view, so no recycling can happen.
Again: this really isn't "performance friendly" and should not be used with big lists with a lot of items!
If you want to know more, feel free to ask in comments or read the reference of Adapter class, especially the parameter convertView of getView method. (http://developer.android.com/reference/android/widget/Adapter.html)
As per my understanding U need to change color of text view in one of the list view row i.e row containing correct answer.
If this is your problem then On Click of button simply clear list view and recreate list view. Not when list view is recreated then check out position of row which contains correct answer. After getting row position simply change color of text view.
For Ex: In your Adapter class check as fallows in your getView();
for ( int i = 0; i < position.length ; i++ )
{
if(position == your_required_position){
{
textview.setColor(Color.BLUE);
}else{
textview.setColor(Color.BLACK);
}
}
In the onClickListener of "Check Answer" button, you need to reset the color:
void onClick (View v){
..............
mAnswerList.getChildAt(currentQuestion).setBackgroundColor
(getResources().getColor(R.color.default_answer_background));
}
you can save the default background somewhere, and when you press next/previous question button you should apply that color. Example:
TypedArray array = getTheme().obtainStyledAttributes(new int[] {
android.R.attr.colorBackground,
android.R.attr.textColorPrimary,
});
int backgroundColor = array.getColor(0, 0xFF00FF);
int textColor = array.getColor(1, 0xFF00FF);
array.recycle();
I found this code online to get background and text color (you can just keep the background part), when the onClick activates just set the background of your view to "backgroundColor"

Android ListView doesn't receive itemClick events after removed with removeAllViews() and restored with addView

I implemented a View stack system for my Android application, which, upon pushing a new View removes the current view in the target layout, stores it in the stack and then adds the new View to the layout.
It works flawlessly until I try storing and then restoring a View containing a ListView. When doing so, the ListView receives no itemClick events, although it does scroll.
The code for the stack system is as follows:
Stack<View> viewStack;
public View pushView(View v) {
// 1. Get reference to main content panel
LinearLayout content = (LinearLayout) findViewById(R.id.contentPanel);
View last = content.getChildAt(0);
// Pushing old to stack
viewStack.push(last);
// 2. Clear it
content.removeAllViews();
// 3. Add new View
content.addView(v);
return last;
}
public View popView() {
if (!viewStack.isEmpty()) {
// 1. Get reference to main content panel
LinearLayout content = (LinearLayout) findViewById(R.id.contentPanel);
View last = content.getChildAt(0);
// 2. Clear it
content.removeAllViews();
// 3. Add last View
content.addView(viewStack.pop());
// Pushing old to stack
return last;
} else {
return null;
}
}
Curiously, the other items in the View that contains the ListView (CheckBoxes and a Button) DO receive clickEvents.
I suspect:
1. The ListView has lost focus so it won't receive those events, or
2. The ListView has been detached of the onItemClickListener
Thanks in advance!
If you have set "clickable" as "true" in you layout in xml file then remove it from every where. then you can try...
If you are declaring anywhere then only. If you are declaring "view_name.setClickable(true)" then remove this line. One more thing if you are using ontouchlistener then always return false. Actually i have faced similar problem in which I was using listview and imageview in listview row. My imageview was receiving click event but listview was not receiving onitemclick event because i had set imageview as clickable in my layout.

Categories

Resources