CheckBox state with BaseAdapter - android

I am programming my first android app. For test purposes I show a list of objects (class cElement) at the mainactivity.
public class cElement {
private Integer Id;
private String Name;
private String Description;
private Boolean Checked;
....
}
The ListView is linked with a custom adapter to the list. The custom adapter extends the BaseAdapter and I inflate the layout of each element from the list.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#+id/standard"
android:textSize="20dp"
android:layout_weight="1">
</TextView>
<TextView
android:id="#+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#+id/standard"
android:textSize="20dp"
android:layout_weight="2">
</TextView>
<TextView
android:id="#+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#+id/standard"
android:textSize="20dp"
android:layout_weight="3">
</TextView>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
android:id="#+id/checkBox"/>
</LinearLayout>
First question: Now I would like to check all CheckBoxes in one step with a button. What is good style?
I update the list directly and call the notifyDataSetChanged method of the adapter
I create a method checkall() within the adapter and call it from outside.
Second question: I would like to know which CheckBox is checked. So I create an eventhandler setOnClickListener and save the position in a variable within the adapter. Is this the right way?

First question: Now I would like to check all CheckBoxes in one step with a button. What is good style?
As it seems you have List which contains boolean Checked for status, you should update all objects of list and notify list again on click of checkAll button.
Second question: I would like to know which CheckBox is checked. So I create an eventhandler setOnClickListener and save the position in a variable within the adapter. Is this the right way?
You should implement listener from fragment/activity, when user clicks on any check box, from adapter, you will get click event in fragment/activity, over there, you update your list item for checked status.
So, in your fragment/activity:
new View.OnClickListener() {
#Override
public void onClick(View v) {
v.getTag()
}
}
And in adapter, set tag for each item with item id/ item object, so when user clicks on any item, you will come to know that item object / item id.

Related

android recyclerview - TextView is not expanding properly with setmaxlines method

I am new to android development. For my learning purpose, I am developing an android application to list the fibonacci numbers in a recycler view. The list gets appended with new numbers as the user scrolls down the recycler view.
The image shows the app displaying the index and respective fibonacci number for the index in the recycler view
This is the layout xml of single item in recycler view.
<?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="wrap_content"
android:orientation="horizontal"
android:clickable="false"
android:focusable="false"
android:padding="5dp">
<TextView
android:id="#+id/info_index"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
<TextView
android:id="#+id/info_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
</LinearLayout>
Excerpt from the adaptor class,
public class ViewHolder extends RecyclerView.ViewHolder {
final TextView index;
final TextView value;
ViewHolder(View itemView) {
super(itemView);
index = itemView.findViewById(R.id.info_index);
value = itemView.findViewById(R.id.info_value);
}
}
public void onBindViewHolder(#NonNull ViewHolder holder, final int position) {
final TextView value = holder.value;
final TextView index = holder.index;
value.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
public void onClick(View v) {
if (value.getMaxLines() == 1000) {
value.setMaxLines(1);
notifyItemChanged(position);
} else {
value.setMaxLines(1000);
notifyItemChanged(position);
}
}
});
Log.d(TAG, "position-value:" + String.valueOf(position));
holder.index.setBackgroundColor(Color.WHITE);
holder.index.setText(String.valueOf(position + 1));
holder.value.setBackgroundColor(Color.WHITE);
holder.value.setText(mData.get(position));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
holder.index.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
holder.value.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
}
}
When I click on the value textview, the textview does not expand on the first click, instead I have to press two times to make it expand. The first time only the flickering happens. I have tried disabling the animator for recyclerview, tried re using the same view in the recycler view, but nothings helps.
My requirement is to expand fibonacci-value textview on click. By default it should be 1 line and when clicked it should show the whole content with multiple lines (as many as required).
currently this happens with two clicks. first time flickering and second time expands.
I believe this is a bug in android code. But just want to confirm here for any solutions that I might have missed.
The reason of the flickering is that, each time an item is clicked, just after setting maxLines, you are calling notifyItemChanged (which is the correct thing to do), but as a result, before redrawing the item onBindViewHolder is called again. So, when it is called again, there should be a way to know current max lines for that item.
Besides, if you try adding lots of items and scroll up and down, you'll see more bugs (since viewholders are reused) Thus, it is important to set/reset maxlines for each item inside onBindViewHolder (but outside click listener)
Secondly, DefaultItemAnimator of RecyclerView uses cross-fade animation when an item changes and by default, it creates two viewholders for that position for cross-fading between the two. So, above, you set a clicklistener on your "value" textview and interfere with the textview inside onClick callback. However, when you later click and inform adapter that the item is changed, it binds the second viewholder instance. So when you click, your click is consumed with the previous "value" instance, and right afterwards a new instance is bound and you set a new clicklistener to this second viewholder instance.
This is one of many reasons that interfering with viewholder items inside the click listener is error-prone. Sometimes people solve this kind of problems with setTag/getTag but I think it is similarly error-prone as well.
I think the easiest solution is to use a POJO (plain old java object) for each item and include the maxLine state in this POJO. Something like FibonacciItem with fields such as int: index, String: fibonacciNumber, boolean : expanded. Then you will provide the list as a list of FibonacciItems. And inside your click listener, you'll update maxlines of the clicked item. Something like this:
public void onClick(View v) {
if (mList.get(position).isExpanded()) {
mList.get(position).setExpanded(false);
notifyItemChanged(position);
} else {
mList.get(position).setExpanded(true);
notifyItemChanged(position);
}
}
And inside your onBindViewHolder, (but outside your click listener) you should set max lines for each item according to this value:
if(mList.get(position).isExpanded(){
holder.value.setMaxLines(1);
} else {
holder.value.setMaxLines(1000);
}
This will solve the issue. Besides, we usually use POJOs (or data classes) for each item in a list. It is easier to manage.
I was able to repeat your issue on my device, the issue you have is that your LinearLayout is consuming your click before it reaches your textview.
Include:
android:clickable="false"
android:focusableInTouchMode="false"
android:focusable="false"
Within your LinearLayout like so:
<?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="wrap_content"
android:orientation="horizontal"
android:clickable="false"
android:focusableInTouchMode="false"
android:focusable="false"
android:padding="5dp">
<TextView
android:id="#+id/info_index"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
<TextView
android:id="#+id/info_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#CDD6D5"
android:gravity="center"
android:maxLines="1" />
</LinearLayout>
Also include:
android:nestedScrollingEnabled="false"
Within your RecyclerView

Reusuing UI for each quest in Quizz app?

I've a scenario where users take a quiz that has 40+ question. Creating 40+ activities is tedious task & i want to know is there anything exists to reuse?
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".MainActivity"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Who founded Apple?"
android:id="#+id/textView"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="49dp" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/editText"
android:text="Fill answers here"
android:layout_below="#+id/textView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="37dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Next"
android:id="#+id/button"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/editText"
android:layout_toEndOf="#+id/editText"
android:clickable="true" />
</RelativeLayout>
In my MainActivity.class, im normally doing like
Button bt = (Button) findViewById(R.id.button);
bt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// ....Go to next activity through Intent...
}
});
Look inside onClick, I need to have each activity for each question that is not good.
Any other solution?
You probably want to create a data structure for your quizes if you haven't. Once you have a datastructure you can think about what kind of adapter you want to use: that is, the mechanism that will link your set of quizes to the UI. For instance, when needing to display a list of something, listview is used with an ArrayAdapter (or BaseAdapter for more custom styled listview).
In the support libraries there is something called a viewpager, which is a set of pages you can swipe horizontally between. You might consider using this.
Or you can create your own view class that has all the views you need to display the quiz, add a method like switchQuiz(Quiz q) which changes the ui with the new quiz information, and use this view in a single activity. Specifically you can take your activity_main.xml layout file, and instead of using it as the layout of an activity, make a custom view (public class QuizView extends RelativeLayout { }) that has a a way to update the ui for the switch quiz functionality. Read here for more information of how to do this: http://trickyandroid.com/protip-inflating-layout-for-your-custom-view/
Create two buttons, prev and next..
prev take you to prev question and next take you next question
Create a class question containing following variable
int number_of_option;
String[] options;
String question;
String answer;
Create a class quiz containing ArrayList<question> list;
add required method to add question, get question , update answer..etc..etc
Create variable current_position_in_UI in quiz class to display that question in activity.
On next/prev button click call a method of quiz to get question fields. something like list.get(position) and update your views with those values on that call. Also update your current_position_in_UI. Hope it helps :)

I have a button in a listview and when clicked all its instances are being clicked

I have a listview containing a button and a textview the textview is being clicked normally; but I have a method for the button click, and when the button is clicked it should change the background image, so by clicking a button all the instances of the button are changing the background image, so I click on fiew buttons in the listview and I scroll down and I see that several button have their background image changed than the place of the buttons with the changed background changes while scrolling the listview up again;
how can I fix that?
the code of the content of my listview:
<?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="horizontal" >
<Button
android:id="#+id/bt_rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:background="#android:drawable/btn_star_big_off"
android:onClick="myClickHandler"/>
<TextView
android:id="#+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/fsinlistview"
/>
</LinearLayout>
the code of my button click is:
public void myClickHandler(View v)
{
Button button = (Button)v.findViewById(R.id.bt_rating);
button.setBackgroundResource(android.R.drawable.btn_star_big_on);
}
You're specifying the background resource for all buttons with the R.id.bt_rating id, where as you'd like to modify the button that was clicked.
You already have a reference to that - it's the View that is passed in.
Change your code to:
public void myClickHandler(View v)
{
v.setBackgroundResource(android.R.drawable.btn_star_big_on);
}
Of course, since you're using a list view, once you scroll through/past this, it won't persist state. What you should be doing is:
updating the data backing your adapter
call notifyDataSetChanged on the adapter
handle the button on/off state display in the adapters getView
Put the setOnclickListener in your getView method of your adapter

How to dynamically add and delete view in android

I want to design below screen
When i press 'Add number' button, it will insert one entry in below scrollable layout. In that when i press 'X' button it should delete that particular row.
How to achieve this??
Any idea??
use
ViewGroup.addView(View view);
to add a view to some layout.
To create a layout dynamically, use:
TextView txtView=new TextView(this);
//Its an example, you can create layouts, buttons, image view, and other components.
To Delete a layout or view dynamically, getParent of layout, and delete, by:
ViewGroup.removeView(View view);
You should use a ListView which is backed by an ArrayList of Objects or Strings. When you want to remove an item from your ListView, remove the object from the ArrayList :
mData.remove(object);
and then notify the ListView that the date has changed :
mAdapter.notifyDataSetChanged();
Use a ListView for displaying the list of patterns
Create a custom layout for each list item. e.g.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/textView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="555*" />
<TextView
android:id="#+id/textView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="matched 5 " />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X" />
</LinearLayout>
Create a Custom Adapter class extending BaseAdapter
It can maintain a list for the patterns to be shown
In the getView method of the custom adapter -
inflate the xml
set the information (like pattern and number of matches) based on the index parameter, using the list
set onclick listener for the button (delete that item from list and call notifyDatasetInvalidated())
return the view.
On "Add Number" add item to the list in the adapter

Display "No Item" message in ListView

I've created some composie UIs in my android apps and there are some ListView controls -among other controls- inside a view. Because of this, I have used "Activity" as my activity base class.
Now I need to display a simple message like "No Item" when the ListView that is bound to my adapter is empty. I know this is possible when using ListActivity but I'm not sure what's the best approach for this?
You can have an empty view without a ListActivity! The correct method is as follows
First add an 'empty view' to your layout XML below your list
...
<ListView
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<TextView
android:id="#+id/empty"
android:text="Empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
/>
...
Next override the onContentChanged method of your activity and set the empty view of your list to your empty view:
#Override
public void onContentChanged() {
super.onContentChanged();
View empty = findViewById(R.id.empty);
ListView list = (ListView) findViewById(R.id.list);
list.setEmptyView(empty);
}
That's it! Android will take care of hiding/showing the list and empty view when you update the adapter.
The Magic
Deciding whether the empty view is shown or not is handled by the superclass of ListView, AdapterView. AdapterView registers a DataSetObserver on the set adapter so it is notified whenever the data is changed. This triggers a call to checkFocus in AdapterView which contains the following lines
if (mEmptyView != null) {
updateEmptyStatus((adapter == null) || adapter.isEmpty());
}
and sets the empty view visibility based on whether the adapter is empty or not.
You're looking for the empty view of a ListActivity:
ListActivity
If you're using ListView you can use the method setEmptyView():
setEmptyView
Just combine your ListView with TextView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="#+id/list"
android:layout_height="fill_parent"
android:layout_width="fill_parent" />
<TextView
android:id="#+id/list_empty"
android:text="No Item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</LinearLayout>
Then check the count of items an chanche visibility on ListView accordingly:
ListView lv = (ListView)findViewById(R.id.list);
lv.setVisibility((adapter.isEmpty())?View.GONE:View.VISIBLE);
If you are using Custom Adapter, you can do this in the overridden notifyDataSetChanged method.
You can use Toast Message for this..
Check the Count of the Adapter value by adapter.getCount()
if(Adapter.getCount()!=0){
List.setAdapter(Adapter);
}else{
Toast.makeText(YourActivityName.this, "No Items Available",Toast.LENGTH_SHORT).show();
}
For the layout code by Joseph, you need to edit the #+id/list and #+id/empty to #android:id/*, like:
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<TextView
android:id="#android:id/empty"
android:text="Empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
/>
This way, you even don't need to override the onContentChanged() function.
The easiest way to achieve this was using a ListFragment instead of a ListActivity. ListFragment has the following convenience method:
setEmptyText("My no items message...");
Besides, using a ListFragment class has other advantages. For example, the possibility to combine it with the new AppCompat library (which you cannot do with ListActivity because you have to extend from ActionBarActivity).

Categories

Resources