Is it possible to change the "style" of a list view once it's displayed? What I want to do is to start with a list view using simple_list_item_1 but switch to simple_list_item_multiple_choice after the list has been populated.
If this isn't possible, can someone please show me how to populate a list view with a text view and checkbox as I'm finding this very difficult to understand.
As always, thanks in advance.
if the two view "simple_list_item_multiple_choice" and "simple_list_item_1" are just different based on the checkbox, then you can just use the "simple_list_item_multiple_choice" where you can make the checkbox hidden on very startup and when the data is loaded you can make the checkbox visible. Or you can show a activity-indicator when the data is loading and after loading the data you can directly port the custom ListView, think this one is better. Some sample code is provided with explanation.
First of all a custom adapter for the listView. This adapter is for each item of the ListView.
//Custom Adapter
private class DataAdapter extends BaseAdapter {
#Override
public int getCount() {
if (channelListTitles.size()>0){
return dataArrayList.size(); //dataArrayList is a ArrayList<String>
}
else {
return 0;
}
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView==null){
convertView = getActivity().getLayoutInflater().inflate(R.layout.item_view,null); //item_view is the custom view for the items of the ListView
}
if (convertView != null) {
((TextView)convertView.findViewById(R.id.item_view_title)).setText(channelListTitles.get(position)); //item_view_title is the TextView
if(isDataLoaded){ //isDataLoaded is a bool value to indicate if data is loaded
CheckBox checkBox = (CheckBox)convertView.findViewById(R.id.checkbox);
checkBox.setVisibility(View.VISIBLE);
}
}
return convertView;
}
}
Now the item_view for each list-item:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/checkbox"
android:visibility="invisible"/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/item_view_title"
android:layout_gravity="center_vertical"/>
</LinearLayout>
Now finally declare a listView on which the upper custom view will be ported. Posting the whole code of the OnCreateView() for better understanding. Here I am using Fragment, you need to change the corresponding methods if you are using Activity:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
if (rootView != null) {
ListView listView = (ListView) rootView.findViewById(R.id.item_list);
DataAdapter itemDataAdapter = new DataAdapter();
listView.setAdapter(itemDataAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// OnItemClickListener goes here
}
});
}
return rootView;
}
Now the fragment_main:
<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"
tools:context=".MainActivity$PlaceholderFragment">
<ListView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/item_list"/>
</RelativeLayout>
The Adapter is an important factor when you want to load data on dynamically. Basically Adapter is the bridge between data and view. Once you have the data you can call the adapter to update the view by using itemDataAdapter.notifyDataSetChanged();.
Yes you can do that, You need to make a xml for the row. And make an adapter class and overwrite getView() and inflate the view here. I always extend BaseAdapter.
# adelphia, i thing you want to use custom listview. Follow this tutorials for custom list view (follow 72 - 84 tutorials).
Related
I deleted almost all code in my project to find a hiding bug. There was a GridView that containing a frame layout, and the layout contained CheckBox. But I couldn't check the first check box.(others worked)
Finally (I think) I found an answer. But this is so weird. When I deleted lines for recycling convertView, the bug was gone. I changed from :
if(convertView == null) {
layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);
} else {
layout = (FrameLayout) convertView;
}
to FrameLayout layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);.
I really have no idea of this stuation. I attach rest codes.
TasteGridAdapter.java:
public class TasteGridAdapter extends BaseAdapter {
Context maincon;
public TasteGridAdapter(Context context) {
maincon = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
FrameLayout layout;
if(convertView == null) {
layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);
} else {
layout = (FrameLayout) convertView;
}
layout.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
return layout;
}
#Override
public int getCount() {
return 3;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
}
onCreate of the activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.taste);
TasteGridAdapter adapter = new TasteGridAdapter(this);
GridView grid = (GridView) findViewById(R.id.taste_grid);
grid.setAdapter(adapter);
}
taste.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">
<GridView
android:layout_weight="1"
android:id="#+id/taste_grid"
android:layout_width="match_parent"
android:layout_height="0dip"
android:columnWidth="87dip"
android:gravity="center"
android:horizontalSpacing="4dip"
android:numColumns="auto_fit"
android:padding="2dip"
android:stretchMode="columnWidth"
android:verticalSpacing="4dip" />
</LinearLayout>
taste_brand.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="87dp"
android:layout_height="58dp">
<CheckBox
android:id="#+id/taste_brand_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</FrameLayout>
I encountered similar problem with first item in GridView. To resolve issue, remove 'new' keyword, and change existing views LayoutParams like that:
LayoutParams lp = layout.getLayoutParams();
lp.height = someHeight;
...do something with these LayoutParams. This hack resolves my issues. Conclusion, try to avoid creation of new LayoutParams object through "new".
layout.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
put this line in this condition,
if(convertView == null) {
}
I was faced same problem, but i try this and its work for me. I hope it also work for you.
What you're experiencing has to do with the way Android recycles views in ListView, GridView, etc. You mention that your first checkbox is uncheckable, while your others remain working. I think you'll notice that the others only appear to work properly, since you haven't handled the recycling properly.
The reason your line
FrameLayout layout = (FrameLayout)View.inflate(maincon, R.layout.taste_brand, null);
seems to fix the problem is because this now inflates the views again each time they are used. I'll admit, when I started with this, re-inflating the views seemed to be the best solution; it entirely defeats the purpose of recycling, however, and you lose all the performance benefits otherwise gained.
So now to fix your problem:
First, I highly recommend using the ViewHolder pattern in conjunction with your BaseAdapter. More information on that can be found here.
Second, you should probably create a boolean array to match all the items in your GridView, and use it to determine whether or not an item should be clicked. Set the value of the corresponding boolean inside your checkbox listener and use that value inside getView(..) to check or uncheck that particular box.
An overall better solution might be to use an array (or list) of models inside your adapter class, each of these containing a boolean field accessible through isChecked and setChecked(boolean). Again, you would use this inside your getView(..) to display the views properly and change the value inside your checkbox OnCheckedChangeListener.
Hope that helps.
As jonstaff says, it's to do with View recycling.
If you're using a custom Adapter class for your GridView View binding, try modifying its getView() method to always instantiate a new View like:
public View getView(int position, View convertView, ViewGroup parent){
SomeView v = new SomeView(context); // <--- here
...
return v;
}
Instead of the typical:
public View getView(int position, View convertView, ViewGroup parent){
SomeView v;
if (convertView == null)
v = new SomeView (context);
else
v= (SomeView)convertView;
...
return v;
}
This may affect performance, but it solved my problem for a small GridView of Buttons.
I have tried to hide items in a custom list adapter. I can hide the visibility of the text but I cannot hide the whole list item. It still shows the dividers etc. I have tried:
tv.setVisibility(View.INVISIBLE);
tv.setVisibility(View.GONE);
convertView.setVisibility(View.INVISIBLE);
convertView.setVisibility(View.GONE);
When I use the convertView i get a null pointer exception.
You can set ContentView with No Element.
In getView() of Your Custom Adapter.
if(condition)
{
convertView=layoutInflater.inflate(R.layout.row_null,null);
return convertView;
}
else
{
convertView=layoutInflater.inflate(R.layout.row_content,null);
return convertView;
}
your XML row_null.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="wrap_content">
</LinearLayout>
You have 3 ways to do this:
Remove the items from your list inside or outside the adapter.
Inside the adapter you can do it in the Constructor.
private List<String> list;
public MyAdapter(Context context, List<String> list) {
list.remove(0);
list.remove(1);
list.remove(<whateverPositionYouLike>);
this.list = list;
}
You need to figure out how many items you want to hide and need to build a similar logic.
#Override
public int getCount() {
// In this adapter, we are supposed to hide the first item of the list view
// Returning 0 if no of items are already 0
if(list.size() <=1)
return 0;
// if number of items are more than one, returning one item less
return list.size() - 1;
}
#Override
public Object getItem(int position) {
// skipping the position
return list.get(position + 1);
}
`
#Override
public View getView(final int position, View v, ViewGroup arg2) {
// this is important, as we are supposed to skip the first item of listview
final int localPosition = position +1;
ViewHolderItem holder;
if (v == null) {
holder = new ViewHolderItem();
v = inflater.inflate(R.layout.list_row, null);
v.setTag(holder);
} else {
holder = (ViewHolderItem) v.getTag();
}
return v;
}
`
Answer provided by #hims_3009
You cannot in the way you are trying to, you'll need to use a custom Adapter and implement in there the logic for showing/not showing a line.
If you want to hide a row in a listview, you need to delete data in that position. For example if you use array adapter and want to hide the row on 5. position. You have to delete line from your array and the call notifyDatasetChanged() method.
(You can delete data from array by using tempArray)
or use this way
private List<String> items = new ArrayList<String>();
// make list a new array, clearing out all old values in it.
for(i=0; i<10; i++)
{
if((i != 5) && (i != 6)){
// if k = 5 or 6, dont add those items to the list
items.add(something[i]); /// whatever your list items are.
}
}
ArrayAdapter<String> itemList = new
ArrayAdapter<String>(this,R.layout.itemlistlayout);
setListAdapter(itemlist);
If you want to hide an entire item you need to build some logic into your getView() method to make it skip over various parts of your data.
lets say you have an ArrayAdapter and I want to hide the data that is at index 2. Your getView() method could look something like this.
#Override
public View getView(int pos, View convertView, ViewGroup, parent){
View mView = convertView;
//TODO: Check if convertView was null, and inflate
// or instantiate if needed.
//Now we are going to set the data
mTxt = mView.findViewById(R.id.mTxt);
if(pos >= 2){
//If position is 2 or above, we ignore it and take the next item.
mTxt.setText(this.getItem(pos + 1).toString());
}else{
//If position is below 2 then we take the current item.
mTxt.setText(this.getItem(pos).toString());
}
return mView;
}
Note that this example is generalized, it is not meant to be able to be dropped directly into your project. I had to make assumptions about some things which I don't know the truth on. You can use the same concept as I've shown here though and modify it to your situation to be able to "hide" rows in your ListView.
If you already have a custom list adapter you can just call notifyDataSetChanged() on the adapter and the adapter itself has of course to implement the logic to filter out the views for the rows you want to hide. These are the methods that come to my mind that need to reflect that logic:
#Override
public Object getItem(int position) {
return mItems.get(position);
}
#Override
public int getCount() {
return mItems.size();
}
#Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
// here goes your logic to hide a row
Additionally you might have to change getItemId() as well.
I think I have a much easier / safer solution: you just have to "embed" your item in a Layout, and change the visibility of this parent layout.
<?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" >
<!-- Embed ListView Item into a "parent" Layout -->
<LinearLayout
android:id="#+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- This is the normal content of your ListView Item -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="World" />
</LinearLayout>
</LinearLayout>
Then in your code just do:
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater li = mActivity.getLayoutInflater();
view = li.inflate(R.layout.my_listview_item, null);
}
LinearLayout parentLayout = (LinearLayout) view.findViewById(R.id.parentLayout);
if (shouldDisplayItem(position)) {
parentLayout.setVisibility(View.VISIBLE);
} else {
parentLayout.setVisibility(View.GONE);
}
return view;
}
This way you always use/reuse the same item, and just hide/show it.
I'm loading phone contacts in a custom ListView. Each row is a checkable LinearLayout containing a CheckedTextView and another TextView.
I'm feeding the list view with a custom ArrayAdapter. My problem is that I can't control CheckedTextViews inside getView(). For example when I try the following
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null){
row = inflater.inflate(layout, parent, false);
}
CheckedTextView checkedTextView = (CheckedTextView) row.findViewById(R.id.checkedTextView);
checkedTextView.setText("A");
checkedTextView.setChecked(true);
return row;
}
That's supposed to check every text view whenever I scroll the list view, but that's not happening. Can anybody tell me how to do it?
EDIT: It's important to check it inside getView(), I can't just check all after setListAdapter()
EDIT2: This is the xml file showing the view of each row
<?xml version="1.0" encoding="utf-8"?>
<com.example.multiplecontacts.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<CheckedTextView
android:id="#+id/checkedTextView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:paddingBottom="0dp"
android:text="CheckedTextView"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/subTextView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Small Text"
android:paddingTop="0dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
</com.example.multiplecontacts.CheckableLinearLayout>
CheckableLinearLayout is a custom layout that extends LinearLayout and implements Checkable as I said before. And I've taken it from here
Did you set a checkMark property for your CheckedTextView in your layout xml?
For example: android:checkMark="?android:attr/listChoiceIndicatorMultiple
CheckedTextView is not just a checkbox with a text. You must also note, that CheckedTextView is not focusable, or clickable without some manipulation (since it was designed for ListView and therefore it's state must be controlled by ListView's setOnItemClickListener)
setChoiceMode should be set for a ListView.
And checking of the row inside adapter's getView should be done via: listView.setItemChecked(position, value)
#Pier-Luc Gendreau and #fox mentioned the solution in comments but not in answer, so posting answer on their behalf.
As mentioned by #AlexOrlov, CheckedTextView is not focusable, or clickable without some manipulation. So what you have to do is set item check using ListView, you can either do it from the adapter itself or from activity/fragment where you have ListView.
To do it from adapter,
public View getView(int position, View convertView, ViewGroup parent)
{
if (//logic) {
((ListView) parent).setItemChecked(position, true);
}
}
From activity/fragment
if (//logic) {
listView.setItemChecked(position, true);
}
When ever you scroll list, every time it calls its getview() method. So, if you have any checkbox or any editbox in listcell it will reinitialize it.
My idea is to store the status (checked or unchecked) of checkbox. So here I used ArrayList first I filled it with false value then on its click event i used to store it actual status.
class MyAdapter extends BaseAdapter implements OnClickListener {
private ArrayList<Boolean> checks=new ArrayList<Boolean>();//Boolean type array to manage check box
private static LayoutInflater inflater=null;
public MyAdapter(Context context, ArrayList<HashMap<String, String>> d)
{
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//fill with false values
for (int i = 0; i < d.size(); i++)
{
checks.add(i, false);
}
}
public int getCount()
{
return data.size();
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.<your layout>, null);
//Checkbox is of button type----android:button="#drawable/btn_check"
//make a selector xml for checkbox
checkBox=(CheckBox)vi.findViewById(R.id.check_box);
checkBox.setTag(Integer.valueOf(position));
checkBox.setOnClickListener(this);
checkBox.setChecked(checks.get(position));
return vi;
}
#Override
public void onClick(View v)
{
int viewId=v.getId();
if(viewId== R.id.check_box)
{
Integer index = (Integer)v.getTag();
boolean state = checks.get(index.intValue());
checks.set(index.intValue(), !state);
}
}
}
Update: Solution 2nd: You can put a boolean variable in your ViewHolder class. This boolean variable will used to define wether item is selected or not.
Hope this should help you.
Here is My GridView implement for android application.
For Program First time it Work well.But after making some Scrolling Up and Down
My problem is when in gridview i scroll it some time put wrong textview in unexpected position.
My Source Code For GridView Adapter is Following.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mGridView=(GridView)findViewById(R.id.gridview_id);
InitializeVariable();
mGridView.setAdapter(new GridAdapter(GridViewExampleActivity.this));
mGridAdapter=new GridAdapter(GridViewExampleActivity.this);
// mGridView.setEnabled(false);
mGridView.setOnLongClickListener(this);
mGridView.setOnItemClickListener(this);
}
public class GridAdapter extends BaseAdapter{
public Context mContext_GridAdapter=getApplicationContext();
GridAdapter(Context context){
this.mContext_GridAdapter=context;
}
#Override
public int getCount() {
return Fix_Grid_View;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RelativeLayout mRelative_Box_Grid;
TextView mTextView;
View v;
if(convertView==null){
mLayoutInflater=LayoutInflater.from(GridViewExampleActivity.this);
v=mLayoutInflater.inflate(R.layout.relative_box_view, null);
mTextView=(TextView)v.findViewById(R.id.txt_number);
mRelative_Box_Grid=(RelativeLayout)v.findViewById(R.id.relative_view);
mRelative_Box_Grid.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT,Device_Height));
mRelative_Box_Grid.setPadding(2, 2, 2, 2);
mTextView.setTextSize(Fix_Text_Size);
mTextView.setText(String.valueOf(position));
mTextView.setId(position);
}else{
v=convertView;
}
return v;
}
}
Here is my Main xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<GridView android:numColumns="3"
android:stretchMode="columnWidth"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="#+id/gridview_id"
android:longClickable="true"
></GridView>
</LinearLayout>
i put same main.xml file in res/layout-land for Landscape mode .
and i also add this line in my AndroidManifest file for handle changes in orientation
android:configChanges="orientation"
and i also implement when we click any item at that position that TextView gone for invisible here is code for that.
#Override
public void onItemClick(AdapterView<?> AdapterView, View view, int position, long arg3) {
int Child_Count;
RelativeLayout mRelativeLayout;
TextView mTextView;
try{
Toast.makeText(GridViewExampleActivity.this, "Position "+ position, 600).show();
Child_Count=AdapterView.getChildCount();
mRelativeLayout=(RelativeLayout) AdapterView.getChildAt(position);
mTextView=(TextView) mRelativeLayout.getChildAt(0);
mTextView.setVisibility(View.INVISIBLE);
// mGridAdapter.notifyDataSetChanged();
// mGridView.setAdapter(mGridAdapter);
Log.v(TAG, "Child Count is"+ Child_Count);
}catch(Exception e){
Log.v("log", " Mes"+TAG +e.getMessage());
}
}
this function work well but fail for some time after making some orientation changes in my Emulator Using Ctrl+F11 Keys.
Here is my Inflater View xml file.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#drawable/rectangle_white"
android:id="#+id/relative_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:text="#string/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="#+id/txt_number"/>
</RelativeLayout>
Here i attach Reference Image of my demo application for problem i Get.
Look at First image when i Scroll up and down value of Textview interchange Unexpected
and when same change in landscape view of android mobile it some time start from 11 that should not happen in such manner.
In your Adapter, try moving these lines:
mTextView=(TextView)v.findViewById(R.id.txt_number);
mTextView.setText(String.valueOf(position));
to be just above your return statement:
return v;
Also, I'm not sure, but i don't think you need the line mTextView.setId(position);, as the position of the view is provided in the onClick callback.
At first glance, I notice the following:
mGridView=(GridView)findViewById(R.id.gridview_id);
InitializeVariable();
mGridView.setAdapter(new GridAdapter(GridViewExampleActivity.this));
mGridAdapter=new GridAdapter(GridViewExampleActivity.this);
Why do you create two GridAdapters?
I would think you only need one like so:
mGridView=(GridView)findViewById(R.id.gridview_id);
InitializeVariable();
// Create the GridAdapter
mGridAdapter=new GridAdapter(GridViewExampleActivity.this);
// Set the Adapter tp the gridview
mGridView.setAdapter(mGirdAdapter);
Furthermore, take a look at your code for the Adapter's getView method.
Android will try to re-use views as much as possible. You only instantiate your views, when convertView == null, e.g. only for the first views that are populated.
After scrolling, Android will try to re-use those views. Hence, convertView will not be null, and the code which sets the text in the views is not executed. Change to this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RelativeLayout mRelative_Box_Grid;
TextView mTextView;
View v;
// Assign the re-usable view to v
v = convertView;
if(v==null){
// Create views if v == null
mLayoutInflater=LayoutInflater.from(GridViewExampleActivity.this);
v=mLayoutInflater.inflate(R.layout.relative_box_view, null);
// NOTE: This will only be executed for the Views that will be
// visible initially (so, for 0 through 11)
}
// Setup your view to show the proper values
// NOTE: This will always be executed
mTextView=(TextView)v.findViewById(R.id.txt_number);
mRelative_Box_Grid=(RelativeLayout)v.findViewById(R.id.relative_view);
mRelative_Box_Grid.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT,Device_Height));
mRelative_Box_Grid.setPadding(2, 2, 2, 2);
mTextView.setTextSize(Fix_Text_Size);
mTextView.setText(String.valueOf(position));
mTextView.setId(position);
return v;
}
Good luck
I've seen example com.example.android.apis.view.List11 from ApiDemos. In that example, each row takes the view android.R.simple_list_item_multiple_choice. Each such view has a TextView and a CheckBox.
Now I want each view to have 2 TextViews and 1 CheckBox, somewhat similar to the List3 example. I tried creating a custom layout file row.xml like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<CheckBox
android:id="#+id/checkbox"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="fill_parent" />
<TextView
android:id="#+id/text_name"
android:textSize="13px"
android:textStyle="bold"
android:layout_toLeftOf="#id/checkbox"
android:layout_alignParentLeft="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/text_phone"
android:textSize="9px"
android:layout_toLeftOf="#id/checkbox"
android:layout_below="#id/text_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
Then in Activity's onCreate(), I do like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Query the contacts
mCursor = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);
startManagingCursor(mCursor);
ListAdapter adapter = new SimpleCursorAdapter(this,
R.layout.row,
mCursor,
new String[] { Phones.NAME, Phones.NUMBER},
new int[] { R.id.text_name, R.id.text_phone });
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
The result kind of looks like what I want, but it looks like the list doesn't know which item of it is selected. Also, I need to click exactly on the CheckBox. In the List11 example, I only need to click on the item row.
So what do I need to do to make a multiple choice list with my custom view for each row? Many thanks.
You have to make your own RelativeLayout that implements the Checkable interface and have a reference to the CheckBox or to the CheckedTextView (or a list if it's multiple choice mode).
Look at this post:
http://www.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/
The answer of Rahul Garg is good for the first time the list is loaded, if you want some rows to be checked depending on the model data, but after that you have to handle the check/uncheck events by yourself.
You can override the onListItemCLick() of the ListActivity to check/uncheck the rows
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
ViewGroup row = (ViewGroup)v;
CheckBox check = (CheckBox) row.findViewById(R.id.checkbox);
check.toggle();
}
If you do so, do not set the ListView to CHOICE_MODE_MULTIPLE, because it makes strange things when calling the function.
To retrieve the list of checked rows, you have to implement a method yourself, calling getCheckItemIds() on the ListView does not work:
ListView l = getListView();
int count = l.getCount();
for(int i=0; i<count; ++i) {
ViewGroup row = (ViewGroup)l.getChildAt(i);
CheckBox check = (Checked) row.findViewById(R.id.ck1);
if( check.isChecked() ) {
// do something
}
}
Each such view has a TextView and a
CheckBox.
No, it doesn't. It has a CheckedTextView.
So what do I need to do to make a
multiple choice list with my custom
view for each row?
Try making the CheckBox android:id value be "#android:id/text1" and see if that helps. That is the ID used by Android for the CheckedTextView in simple_list_item_multiple_choice.
The solution is to create a custom View that implements the Clickable interface.
public class OneLineCheckableListItem extends LinearLayout implements Checkable {
public OneLineCheckableListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
private boolean checked;
#Override
public boolean isChecked() {
return checked;
}
#Override
public void setChecked(boolean checked) {
this.checked = checked;
ImageView iv = (ImageView) findViewById(R.id.SelectImageView);
iv.setImageResource(checked ? R.drawable.button_up : R.drawable.button_down);
}
#Override
public void toggle() {
this.checked = !this.checked;
}
}
And create a custom layout for the list items using the new widget.
<?xml version="1.0" encoding="utf-8"?>
<ax.wordster.OneLineCheckableListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:background="#drawable/selector_listitem"
android:orientation="horizontal" >
<ImageView
android:id="#+id/SelectImageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="#drawable/button_friends_down" />
<TextView
android:id="#+id/ItemTextView"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:gravity="center"
android:text="#string/___"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#color/text_item" />
</ax.wordster.OneLineCheckableListItem>
Then create a new custom Adapter using the layout above.
It is possible by some trick
in your ListActivtyClass in method
protected void onListItemClick(ListView l, View v, int position, long id) {
//just set
<your_model>.setSelected(true);
}
now in you custom Adapter
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(textViewResourceId, parent, false);
}
if (<your_model>.isSelected()) {
convertView.setBackgroundColor(Color.BLUE);
} else {
convertView.setBackgroundColor(Color.BLACK);
}
return convertView;
}
this way you can customize the view in adapter when the item is selected in the list.
Simple example how to get a custom layout to work as custom checkbox:
private class FriendsAdapter extends ArrayAdapter<WordsterUser> {
private Context context;
public FriendsAdapter(Context context) {
super(context, R.layout.listitem_oneline);
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int pos = position;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rv = inflater.inflate(R.layout.listitem_oneline, parent, false);
rv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
boolean checked = friendsListView.isItemChecked(pos);
friendsListView.setItemChecked(pos, !checked);
}
});
WordsterUser u = getItem(position);
TextView itw = (TextView) rv.findViewById(R.id.ItemTextView);
itw.setText(u.userName + " (" + u.loginName + ")");
ImageView iv = (ImageView) rv.findViewById(R.id.SelectButton);
if (friendsListView.isItemChecked(position)) {
iv.setImageResource(R.drawable.downbutton);
} else {
iv.setImageResource(R.drawable.upbutton);
}
return rv;
}
}
I found it very useful this little code: http://alvinalexander.com/java/jwarehouse/apps-for-android/RingsExtended/src/com/example/android/rings_extended/CheckableRelativeLayout.java.shtml
It is a great addition to #ferdy182 's http://www.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/ content.
Got the solution ... You can get the clicks on the views (like checkboxes in custom layouts of row) by adding listener to each of them in the adapter itself while you return the converted view in getView(). You may possibly have to pass a reference of list object if you intent to get any list specific info. like row id.
I want to confirm that the Pritam's answer is correct. You need an onClickListener on each list's item (define it in the adapter's getView()).
You can create a new onClickListener() for each item, or have the adapter implement onClickListener() - in this case the items must be tagged for the listener to know, which item it is operating on.
Relying on the list onItemClickListener() - as someone advised in another thread - will not work as the CheckBox will intercept the click event so the list will not get it.
And finally #Rahul and JVitella:
The situation is that the CheckBox on a list item must be clickable and checkable independently from the list item itself. Therefore the solution is as I just described above.