I'm looking now a few days for a solution for clickable items in a listView.
First I came across this:
developer.android.com/resources/articles/touch-mode.html
and found that it's doesn't have the "normal" onListItemClick() behavouir.
Then I came across of this code:
http://www.androidsnippets.org/snippets/125/
// LINE 296-321
#Override
protected ViewHolder createHolder(View v) {
// createHolder will be called only as long, as the ListView is not filled
// entirely. That is, where we gain our performance:
// We use the relatively costly findViewById() methods and
// bind the view's reference to the holder objects.
TextView text = (TextView) v.findViewById(R.id.listitem_text);
ImageView icon = (ImageView) v.findViewById(R.id.listitem_icon);
ViewHolder mvh = new MyViewHolder(text, icon);
// Additionally, we make some icons clickable
// Mind, that item becomes clickable, when adding a click listener (see API)
// so, it is not necessary to use the android:clickable attribute in XML
icon.setOnClickListener(new ClickableListAdapter.OnClickListener(mvh) {
public void onClick(View v, ViewHolder viewHolder) {
// we toggle the enabled state and also switch the icon
MyViewHolder mvh = (MyViewHolder) viewHolder;
MyData mo = (MyData) mvh.data;
mo.enable = !mo.enable; // toggle
ImageView icon = (ImageView) v;
icon.setImageBitmap(
mo.enable ? ClickableListItemActivity.this.mIconEnabled
: ClickableListItemActivity.this.mIconDisabled);
}
});
While debugging I noticed the parameter View v is a TextView and not a "normal" View and then of course:
TextView text = (TextView) v.findViewById(R.id.listitem_text);
returnes null and I get a NullPointerException...
Any ideas why? And how I can solve this?
Thanks in advance! :)
How do you create your instance of ClickableListAdapter ?
When you create your list adapter, you have to pass a resource id viewId, this should be a layout which will be inflated later.
public ClickableListAdapter(Context context, int viewid, List objects) {
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
mDataObjects = objects;
mViewId = viewid;
Below, the code inflate the xml layout passed to the constructor and call createHolder.
view = mInflater.inflate(mViewId, null);
// call the user's implementation
holder = createHolder(view);
So make sure that when instantiating your ClickableListAdapter, you pass a layout instead of an id
Edit
You have to create a xml layout with the following which is taken from the link you have provided:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
>
<TextView android:text="Text" android:id="#+id/listitem_text"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
></TextView>
<ImageView android:id="#+id/listitem_icon"
android:src="#drawable/globe2_32x32"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="32px"
android:maxHeight="32px"
>
</ImageView>
</LinearLayout>
If you call it mylistrow.xml in the layout directory, so you construct your adapter as :
adapter = new MyClickableChannelListAdapter(this, R.layout.mylistrow, channelList);
setListAdapter(adapter);
List items should be clickable right out of the box. You can check how lists are coded by looking at ApiDemos project code. It should be present on your local machine since it is a part of the SDK. I have it at <root_sdk_folder>\platforms\android-2.0.1\samples\ApiDemos.
Related
I have a 'ChecklistItem' class that has the following properties:
private CheckBox checkBox;
private ImageButton noteButton;
private TextView vitalField;
I have an onClick Listener for my checkbox. Now the problem is, when I click on that checkbox and the OnClick() method gets called, how can I figure out what ChecklistItem that checkbox is a part of?
Whenever I click on a checkbox, I want to add the ChecklistItem that the checkbox is a part of to an array, but the OnClick() only knows about the checkbox that called it.
How can I get around this?
Ok so this answer is according to the "long discussion" we had
let's assume you want to make a - re usable - view of your list and you wrote a separate xml layout file called list_item as the following:
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/checkbox"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/text_view"/>
so now let's assume you are in the activity or fragment or wherever you want to host your view , NOW I have to point out this is just an example , usually a list view is what you would need in this case but again I have very little details about your app so I'm going to keep it simple
Assuming you have a vertical linear layout and you want to add these "rows" to it, each row represents one of your custom view
LinearLayout layout = findViewById(R.id.layout);
LayoutInflater inflater = LayoutInflater.from(this); // This inflater is responsible of creating instances of your view
View myView = inflater.inflate(R.layout.list_item, layout, false); // This view objects is the view you made in your xml file
CheckBox checkBox = (CheckBox) myView.findViewById(R.id.checkbox);
TextView textView = (TextView) myView.findViewById(R.id.text_view);
checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//if checkbox is checked enable textview for example
// here you have a reference to all the views you just created
// Weather you want to save them in a class together that's up to you and your app's logic
}
});
layout.addView((myView));
if the list is might exceed the screen height you may want to wrap your linear layout in a scroll view.
BTW: ListView is just a neat way to do this automatically by defining how you want each row to appear, and of course it manages your views for you and recycle them when they get of screen, but I just wanted to point out the concept.
Hope this helps you
I have created a custom ListView using a custom adapter. I have an xml file that defines each row, and each row has a checkbox defined in this xml file. My app is a judging app where each item on the ListView is a "task" that counts for a certain number of points. The idea is that if the task is completed, then the judge clicks the checkbox, and the score of that task is added to an overall score.
Unfortunately, I see no way to get this value associated with the checkbox. Is there a way to do this? I'll post some code, and I hope it's enough to get the general idea of my issue.
The XML file for the row:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/score_list_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<CheckBox
android:id="#+id/score_box"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:paddingTop="30dp"
android:scaleX="2"
android:scaleY="2"
android:onClick="chkBoxClicked" />
<TextView
android:id="#+id/subtask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="#id/score_box"
android:paddingRight="30dp"
android:textSize="20sp"/>
<TextView
android:id="#+id/max_points"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/subtask" />
</RelativeLayout>
The method from the activity that creates the list:
...
public void createScoringList() {
ListView scoreList = (ListView) findViewById(R.id.score_list);
ListView scoreListPartial = (ListView) findViewById(R.id.score_list_partial);
ArrayList<ScoringInfo> objList = new ArrayList<ScoringInfo>();
ArrayList<ScoringInfo> objListPartial = new ArrayList<ScoringInfo>();
ScoringInfo scrInfo;
for (int i = 0; i < subTaskList.size(); i++) {
subtask_num = subTaskList.get(i).subtask_num;
max_points = subTaskList.get(i).max_points;
partial_points_allowed = subTaskList.get(i).partial_points_allowed;
task_name = subTaskList.get(i).task_name;
scrInfo = new ScoringInfo();
scrInfo.setMaxPoints("Max Points: " + max_points);
scrInfo.setSubtask(task_name);
if (partial_points_allowed == 1)
objListPartial.add(scrInfo);
else
objList.add(scrInfo);
}
scoreList.setAdapter(new ScoreListAdapter(objList , this));
scoreListPartial.setAdapter(new ScoreListAdapter2(objListPartial, this));
}
If more code is required for clarity, ask, and I will provide. I just didn't want to overflow the question with loads of code that I think may be unnecessary.
You can either store this value in your model (I think it's called ScoringInfo) or you can assign this value to every checkbox using setTag("score", value) method and read it by calling getTag("score").
You can set and read tag in your adapter class like this. You adapter shall implement OnClickListener and manage list of ScoringInfo items.
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(this)
.inflate(R.layout.<your_layout>, parent, false);
}
ScoringInfo item = this.getItem(position);
CheckBox checkBox = (CheckBox) convertView.findViewById(R.id.checkbox_id);
checkBox.setTag("score", item.max_points);
checkBox.setOnClickListener(this);
}
public void onClick(View view) {
if (view instanceof CheckBox) {
boolean checked = ((CheckBox) view).isChecked();
int score = view.getTag("score");
// do the rest
}
}
I want my ListView to contain buttons, but setting the button's xml property, onClick="myFunction" and then placing a public void myFunction(android.view.View view) method in the activity causes an NoSuchMethodException (the stack trace is null) to be thrown, as although the onclick listener is there, it doesn't fire myFunction(...) and cause the activity to close.
How do I create a custom Adapter that connects a View.OnClickListener to a button on each row of a ListView?
My ListView is created as follows...
[activity.java content..]
public void myFunction(android.view.View view)
{
//Do stuff
}
[activity.xml content..]
<LinearLayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".FrmCustomerDetails" >
<ListView android:id="#+id/LstCustomerDetailsList" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:clickable="true" android:clipChildren="true" android:divider="#null" android:dividerHeight="0dp" android:fastScrollEnabled="true" android:footerDividersEnabled="false" android:headerDividersEnabled="false" android:requiresFadingEdge="vertical" android:smoothScrollbar="true" />
</LinearLayout>
[activity_row_item.xml content..]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="#+id/Llt" android:layout_width="match_parent" android:layout_height="match_parent" >
<Button android:id="#+id/Btn" android:text="Click me" android:onClick="myFunction" />
</LinearLayout>
Here is how to create the custom Adapter, connecting View.OnClickListener to a ListView with a button per row...
1. Create a layout for a typical row
In this case, the row is composed of three view components:
name (EditText)
value (EditText:inputType="numberDecimal")
delete (Button)
Xml
pay_list_item.xml layout is as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<EditText
android:id="#+id/pay_name"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="2"
android:hint="Name" />
<EditText
android:id="#+id/pay_value"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:inputType="numberDecimal"
android:text="0.0" />
<Button
android:id="#+id/pay_removePay"
android:layout_width="100dp"
android:layout_height="fill_parent"
android:text="Remove Pay"
android:onClick="removePayOnClickHandler" />
</LinearLayout>
Note: the button has onClick handler defined in xml layout file, because we want to refer its action to a specific list item.
Doing this means that the handler will be implemented in Activity file and each button will know which list item it belongs to.
2. Create list item adapter
This is the java class that is the controller for pay_list_item.xml.
It keeps references for all of its views, and it also puts these references in tags, extending the ArrayAdapter interface.
The Adapter:
public class PayListAdapter extends ArrayAdapter<Payment> {
private List<Payment> items;
private int layoutResourceId;
private Context context;
public PayListAdapter(Context context, int layoutResourceId, List<Payment> items) {
super(context, layoutResourceId, items);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PaymentHolder holder = null;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new PaymentHolder();
holder.Payment = items.get(position);
holder.removePaymentButton = (ImageButton)row.findViewById(R.id.pay_removePay);
holder.removePaymentButton.setTag(holder.Payment);
holder.name = (TextView)row.findViewById(R.id.pay_name);
holder.value = (TextView)row.findViewById(R.id.pay_value);
row.setTag(holder);
setupItem(holder);
return row;
}
private void setupItem(PaymentHolder holder) {
holder.name.setText(holder.Payment.getName());
holder.value.setText(String.valueOf(holder.Payment.getValue()));
}
public static class PaymentHolder {
Payment Payment;
TextView name;
TextView value;
ImageButton removePaymentButton;
}
}
Here we list the Payment class items.
There are three most important elements here:
PayListAdapter constructor: sets some private fields and calls superclass constructor. It also gets the List of Payment objects. Its implementation is obligatory.
PaymentHolder: static class that holds references to all views that I have to set in this list item. I also keep the Payment object that references to this particular item in list. I set it as tag for ImageButton, that will help me to find the Payment item on list, that user wanted to remove
Overriden getView method: called by superclass. Its goal is to return the single List row. We create its fields and setup their values and store them in static holder. Holder then is put in row’s tag element. Note that there is a performance issue, as the row is being recreated each time it is displayed. I used to add some flag in holder like isCreated, and set it to true after row was already created. then you can add if statement and read tag’s holder instead of creating it from scratch.
Payment.java is quite simple as for now and it looks a bit like BasicNameValuePair:
public class Payment implements Serializable {
private String name = "";
private double value = 0;
public Payment(String name, double value) {
this.setName(name);
this.setValue(value);
}
...
}
There are additional gets and sets for each private field not shown.
3. Add ListView to the activity layout xml file
In its simpliest form, it will be enough to add this view to activity layout:
<ListView
android:id="#+id/EnterPays_PaysList"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
4. Set up adapter to this list view in Activity Java code
In order to display items in ListView you need to set up its adapter and map it to some other ArrayList of Payment objects (as I am extending an Array adapter here). Here is code that is responsible for binding adapter to editPersonData.getPayments() ArrayList:
PayListAdapter adapter = new PayListAdapter(AddNewPerson.this, R.layout.pay_list_item, editPersonData.getPayments());
ListView PaysListView = (ListView)findViewById(R.id.EnterPays_PaysList);
PaysListView.setAdapter(adapter);
5. Adding / removing items to ListView (and its adapter)
Adapter is handled just like any other ArrayList, so adding new element to it is as simple as:
Payment testPayment = new Payment("Test", 13);
adapter.add(testPayment);
adapter.remove(testPayment);
6. Handle Remove Payment button click event
In an activity’s code, where ListView is displayed, add public method that will handle remove button click action. The method name has to be exactly the same as it was in pay_list_item.xml:
android:onClick="removePayOnClickHandler"
The method body is as follows:
public void removePayOnClickHandler(View v) {
Payment itemToRemove = (Payment)v.getTag();
adapter.remove(itemToRemove);
}
The Payment object was stored in ImageButton’s Tag element. Now it is enough to read it from Tag, and remove this item from the adapter.
7. Incorporate remove confirmation dialog window
Probably you need also make sure that user intentionally pressed the remove button by asking him additional question in confirmation dialog.
Dialogue
a) Create dialog’s id constant
This is simply dialog’s ID. it should be unique among any other dialog window that is handled by current activity. I set it like that:
protected static final int DIALOG_REMOVE_CALC = 1;
protected static final int DIALOG_REMOVE_PERSON = 2;
b) Build dialog
I use this method to build dialog window:
private Dialog createDialogRemoveConfirm(final int dialogRemove) {
return new AlertDialog.Builder(getApplicationContext())
.setIcon(R.drawable.trashbin_icon)
.setTitle(R.string.calculation_dialog_remove_text)
.setPositiveButton(R.string.calculation_dialog_button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
handleRemoveConfirm(dialogRemove);
}
})
.setNegativeButton(R.string.calculation_dialog_button_cancel, null)
.create();
}
AlertDialog builder pattern is utilized here. I do not handle NegativeButton click action – by default the dialog is just being hidden. If dialog’s confirm button is clicked, my handleRemoveConfirm callback is called and action is performed based on dialog’s ID:
protected void handleRemoveConfirm(int dialogType) {
if(dialogType == DIALOG_REMOVE_PERSON){
calc.removePerson();
}else if(dialogType == DIALOG_REMOVE_CALC){
removeCalc();
}
}
c) Show Dialog
I show dialog after my remove button click. The showDialog(int) is Android’s Activity’s method:
OnClickListener removeCalcButtonClickListener = new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_REMOVE_CALC);
}
};
the showDialog(int) method calls onCreateDialog (also defined in Activity’s class). Override it and tell your app what to do if the showDialog was requested:
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_REMOVE_CALC:
return createDialogRemoveConfirm(DIALOG_REMOVE_CALC);
case DIALOG_REMOVE_PERSON:
return createDialogRemoveConfirm(DIALOG_REMOVE_PERSON);
}
}
Take a look at this blog post I wrote on exactly this matter:
Create custom ArrayAdapter
There are comments that explain every action I make in the adapter.
Here is the explanation in short:
So lets for example take a row where you want to place a CheckBox, ImageView
and a TextView while all of them are clickable. Meaning that you can click the
row it self for going to another Actvity for more details on the row, check its
CheckBox or press the ImageView to perform another operation.
So what you should do is:
1. First create an XML layout file for your ListView row:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/cbCheckListItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp" />
<TextView
android:id="#+id/tvItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="item string" />
<ImageView
android:id="#+id/iStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#drawable/ic_launcher"
android:src="#drawable/ic_launcher" />
</LinearLayout>
2. Second in your java code define a ViewHolder, a ViewHolder
is designed to hold the row views and that way operating more quickly:
static class ViewHolder
{
TextView title;
CheckBox checked;
ImageView changeRowStatus;
}
3. Now we have to define CustomArrayAdapter, using the array adapter
we can define precisely what is the desired output for each row based on the content of this
row or it’s position. We can do so by overriding the getView method:
private class CustomArrayAdapter extends ArrayAdapter<RowData>
{
private ArrayList<RowData> list;
//this custom adapter receives an ArrayList of RowData objects.
//RowData is my class that represents the data for a single row and could be anything.
public CustomArrayAdapter(Context context, int textViewResourceId, ArrayList<RowData> rowDataList)
{
//populate the local list with data.
super(context, textViewResourceId, rowDataList);
this.list = new ArrayList<RowData>();
this.list.addAll(rowDataList);
}
public View getView(final int position, View convertView, ViewGroup parent)
{
//creating the ViewHolder we defined earlier.
ViewHolder holder = new ViewHolder();)
//creating LayoutInflator for inflating the row layout.
LayoutInflater inflator = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//inflating the row layout we defined earlier.
convertView = inflator.inflate(R.layout.row_item_layout, null);
//setting the views into the ViewHolder.
holder.title = (TextView) convertView.findViewById(R.id.tvItemTitle);
holder.changeRowStatus = (ImageView) convertView.findViewById(R.id.iStatus);
holder.changeRowStatus.setTag(position);
//define an onClickListener for the ImageView.
holder.changeRowStatus.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Toast.makeText(activity, "Image from row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
holder.checked = (CheckBox) convertView.findViewById(R.id.cbCheckListItem);
holder.checked.setTag(position);
//define an onClickListener for the CheckBox.
holder.checked.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
//assign check-box state to the corresponding object in list.
CheckBox checkbox = (CheckBox) v;
rowDataList.get(position).setChecked(checkbox.isChecked());
Toast.makeText(activity, "CheckBox from row " + position + " was checked", Toast.LENGTH_LONG).show();
}
});
//setting data into the the ViewHolder.
holder.title.setText(RowData.getName());
holder.checked.setChecked(RowData.isChecked());
//return the row view.
return convertView;
}
}
4. Now you need to set this adapter, as the adapter of your ListView.
this ListView can be created in java or using an XML file, in this case I’m using a list that was
defined in the XML file using the “list” id:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
ListView list = (ListView)findViewById(R.id.list);
CustomArrayAdapter dataAdapter = new CustomArrayAdapter(this, R.id.tvItemTitle, rowDataList);
list.setAdapter(dataAdapter);
}
5. Finally if we want to be able to press the row it self and not only a certain view in it
we should assign an onItemClickListener to the ListView:
list.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view,int position, long id)
{
Toast.makeText(activity, "row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
First, the way of adding listeners in xml using onClick="function" is deprecated. You need a ViewHolder class to link the button in the xml to your java code. Then you can implement onClickListener for that.
Inside your getView() implementation of CustomAdapter, you can try like below.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.xxxxx, null);
holder = new ViewHolder();
holder.invite = (Button) convertView.findViewById(R.id.button);
} else {
holder = (ViewHolder) convertView.getTag();
}
final int pos = position;
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleClick(pos);
}
});
}
class ViewHolder {
Button button;
}
I've been trying to create a simple class that implements the ViewSwitcher.ViewFactory interface basing on the project developed in "Sams Teach Yourself Android Application Development in 24 Hours". In the example the makeView() method inflates a layout in order to obtain a View. However, I wanted to do it programmatically and it didn't work.
The onCreate() method of the activity looks like this:
private TextSwitcher mQuestionText;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
mQuestionText = (TextSwitcher) findViewById(R.id.MyTextSwitcher);
mQuestionText.setFactory(new MyTextSwitcherFactory());
mQuestionText.setCurrentText("blablabla");
}
The proposed solution goes like this:
private class MyTextSwitcherFactory implements ViewSwitcher.ViewFactory {
public View makeView() {
TextView textView = (TextView) LayoutInflater.from(
getApplicationContext()).inflate(
R.layout.text_switcher_view,
mQuestionText, false);
return textView;
}
}
The resource file is:
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:textColor="#color/title_color"
android:textSize="#dimen/game_question_size"
android:gravity="center"
android:text="Testing String"
android:layout_height="match_parent"
android:padding="10dp">
</TextView>
While I wanted to it like this:
private class MyImageSwitcherFactory implements ViewSwitcher.ViewFactory {
public View makeView() {
TextView textView = new TextView(getApplicationContext());
textView.setLayoutParams(new TextSwitcher.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
textView.setTextColor(R.color.title_color);
textView.setTextSize(R.dimen.game_question_size);
textView.setGravity(Gravity.CENTER);
textView.setText("Test");
return textView;
}
}
I get the "blablabla" text displayed when I use the inflate method but not doing it programmatically. Could you point to an error in my code, please?
mQuestionText = (TextSwitcher) findViewById(R.id.MyTextSwitcher);
You're finding the view by the id MyTextSwitcher. Make sure you set that id if you're creating the view programmatically.
view.setId(R.id.MyTextSwitcher);
Update:
Whoops, didn't read your code carefully enough. You're right, since you're inflating from the XML, your ID should be set properly already. What you probably are missing is actually adding the view to the view hierarchy. You need to find a parent ViewGroup (e.g. a LinearLayout or a 'RelativeLayout, etc.) under which you want to putMyTextSwitcher, let's call itroot` and add it with something like:
root.addView(view);
I found out what was wrong with the code I provided. This is wrong since it gives the ID of the resource as the function parameter instead of the value itself.
textView.setTextColor(R.color.title_color);
textView.setTextSize(R.dimen.game_question_size);
This is correct:
textView.setTextColor(getApplicationContext().getResources().getColor(R.color.title_color));
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getApplicationContext().getResources().getDimension(R.dimen.game_question_size));
So I have a custom ListView object. The list items have two textviews stacked on top of each other, plus a horizontal progress bar that I want to remain hidden until I actually do something. To the far right is a checkbox that I only want to display when the user needs to download updates to their database(s). When I disable the checkbox by setting the visibility to Visibility.GONE, I am able to click on the list items. When the checkbox is visible, I am unable to click on anything in the list except the checkboxes. I've done some searching but haven't found anything relevant to my current situation. I found this question but I'm using an overridden ArrayAdapter since I'm using ArrayLists to contain the list of databases internally. Do I just need to get the LinearLayout view and add an onClickListener like Tom did? I'm not sure.
Here's the listview row layout XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="#+id/UpdateNameText"
android:layout_width="wrap_content"
android:layout_height="0dip"
android:layout_weight="1"
android:textSize="18sp"
android:gravity="center_vertical"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:id="#+id/UpdateStatusText"
android:singleLine="true"
android:ellipsize="marquee"
/>
<ProgressBar android:id="#+id/UpdateProgress"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:indeterminateOnly="false"
android:progressDrawable="#android:drawable/progress_horizontal"
android:indeterminateDrawable="#android:drawable/progress_indeterminate_horizontal"
android:minHeight="10dip"
android:maxHeight="10dip"
/>
</LinearLayout>
<CheckBox android:text=""
android:id="#+id/UpdateCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
And here's the class that extends the ListActivity. Obviously it's still in development so forgive the things that are missing or might be left laying around:
public class UpdateActivity extends ListActivity {
AccountManager lookupDb;
boolean allSelected;
UpdateListAdapter list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lookupDb = new AccountManager(this);
lookupDb.loadUpdates();
setContentView(R.layout.update);
allSelected = false;
list = new UpdateListAdapter(this, R.layout.update_row, lookupDb.getUpdateItems());
setListAdapter(list);
Button btnEnterRegCode = (Button)findViewById(R.id.btnUpdateRegister);
btnEnterRegCode.setVisibility(View.GONE);
Button btnSelectAll = (Button)findViewById(R.id.btnSelectAll);
btnSelectAll.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
allSelected = !allSelected;
for(int i=0; i < lookupDb.getUpdateItems().size(); i++) {
lookupDb.getUpdateItem(i).setSelected(!lookupDb.getUpdateItem(i).isSelected());
}
list.notifyDataSetChanged();
// loop through each UpdateItem and set the selected attribute to the inverse
} // end onClick
}); // end setOnClickListener
Button btnUpdate = (Button)findViewById(R.id.btnUpdate);
btnUpdate.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
} // end onClick
}); // end setOnClickListener
lookupDb.close();
} // end onCreate
#Override
protected void onDestroy() {
super.onDestroy();
for (UpdateItem item : lookupDb.getUpdateItems()) {
item.getDatabase().close();
}
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
UpdateItem item = lookupDb.getUpdateItem(position);
if (item != null) {
item.setSelected(!item.isSelected());
list.notifyDataSetChanged();
}
}
private class UpdateListAdapter extends ArrayAdapter<UpdateItem> {
private List<UpdateItem> items;
public UpdateListAdapter(Context context, int textViewResourceId, List<UpdateItem> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = null;
if (convertView == null) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = li.inflate(R.layout.update_row, null);
} else {
row = convertView;
}
UpdateItem item = items.get(position);
if (item != null) {
TextView upper = (TextView)row.findViewById(R.id.UpdateNameText);
TextView lower = (TextView)row.findViewById(R.id.UpdateStatusText);
CheckBox cb = (CheckBox)row.findViewById(R.id.UpdateCheckBox);
upper.setText(item.getName());
lower.setText(item.getStatusText());
if (item.getStatusCode() == UpdateItem.UP_TO_DATE) {
cb.setVisibility(View.GONE);
} else {
cb.setVisibility(View.VISIBLE);
cb.setChecked(item.isSelected());
}
ProgressBar pb = (ProgressBar)row.findViewById(R.id.UpdateProgress);
pb.setVisibility(View.GONE);
}
return row;
}
} // end inner class UpdateListAdapter
}
edit: I'm still having this problem. I'm cheating and adding onClick handlers to the textviews but it seems extremely stupid that my onListItemClick() function is not being called at all when I am not clicking on my checkbox.
The issue is that Android doesn't allow you to select list items that have elements on them that are focusable. I modified the checkbox on the list item to have an attribute like so:
android:focusable="false"
Now my list items that contain checkboxes (works for buttons too) are "selectable" in the traditional sense (they light up, you can click anywhere in the list item and the "onListItemClick" handler will fire, etc).
EDIT: As an update, a commenter mentioned "Just a note, after changing the visibility of the button I had to programmatically disable the focus again."
In case you have ImageButton inside the list item you should set the descendantFocusability value to 'blocksDescendants' in the root list item element.
android:descendantFocusability="blocksDescendants"
And the focusableInTouchMode flag to true in the ImageButton view.
android:focusableInTouchMode="true"
I've had a similar issue occur and found that the CheckBox is rather finicky in a ListView. What happens is it imposes it's will on the entire ListItem, and sort of overrides the onListItemClick. You may want to implement a click handler for that, and set the text property for the CheckBox as well, instead of using the TextViews.
I'd say look into this View object as well, it may work better than the CheckBox
Checked Text View
use this line in the root view of the list item
android:descendantFocusability="blocksDescendants"