Is it possible to customize preference-header layout? - android

I have found similar questions, but can't find a specific answer that is up-to-date.
I'm using <preference-header>, as per 3.0+ settings design guidelines ( I target 4.1.2+) to build my headers; I want to set a custom layout to these headers. Note that I don't want to fall back to the old PreferenceScreen method as described here, because I don't support older Android version.
As far as I could research, this layout is held by a private member of the PreferenceActivity class, and it's retrieved with a styleable attribute that doesn't seem publicly accessible:
private int mPreferenceHeaderItemResId = 0;
...
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
...
TypedArray sa = obtainStyledAttributes(null,
com.android.internal.R.styleable.PreferenceActivity,
com.android.internal.R.attr.preferenceActivityStyle,
0);
...
mPreferenceHeaderItemResId = sa.getResourceId(
com.android.internal.R.styleable.PreferenceActivity_headerLayout,
com.android.internal.R.layout.preference_header_item);
...
}
This resource is then passed to a private Adapter to populate the header ListView.
Is there a way to pass a different layout resource?

UPDATE 14.4.2016: there was problem with recreate by savedInstanceState, but I found another similar solution from which I used setListAdapter method code (I modified the code below).
I solve this problem just too. I don't know if the following solution is correct, but was the fastest. Because PreferenceActivity is child of ListActivity, you can override setListAdapter method for using own adapter for header items. This is ugly hack, because setListAdapter method is called in PreferenceActivity.onCreate() with sets adapter parameter to new instance of HeaderAdapter, so following adjustment ignore this instance.
#Override
public void setListAdapter(ListAdapter adapter) {
int i, count;
if (mHeaders == null) {
mHeaders = new ArrayList<>();
// When the saved state provides the list of headers, onBuildHeaders is not called
// so we build it from the adapter given, then use our own adapter
count = adapter.getCount();
for (i = 0; i < count; ++i) {
mHeaders.add((Header) adapter.getItem(i));
}
}
super.setListAdapter(new CustomHeaderAdapter(this, mHeaders, R.layout.preference_header_item, true));
}
mHeaders property is defined as class member
private List<Header> mHeaders;
and is assigned in onBuildHeaders:
#Override
public void onBuildHeaders(List<Header> target) {
mHeaders = target;
loadHeadersFromResource(R.xml.preference_headers, target);
...
}
I copied and modified adapter inner class and layout from SDK source:
private static class CustomHeaderAdapter extends ArrayAdapter<Header> {
private static class HeaderViewHolder {
ImageView icon;
TextView title;
TextView summary;
}
private LayoutInflater mInflater;
private int mLayoutResId;
private boolean mRemoveIconIfEmpty;
public CustomHeaderAdapter(Context context, List<Header> objects, int layoutResId,
boolean removeIconBehavior) {
super(context, 0, objects);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLayoutResId = layoutResId;
mRemoveIconIfEmpty = removeIconBehavior;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
View view;
if (convertView == null) {
view = mInflater.inflate(mLayoutResId, parent, false);
holder = new HeaderViewHolder();
holder.icon = (ImageView) view.findViewById(R.id.icon);
holder.title = (TextView) view.findViewById(R.id.title);
holder.summary = (TextView) view.findViewById(R.id.summary);
view.setTag(holder);
} else {
view = convertView;
holder = (HeaderViewHolder) view.getTag();
}
// All view fields must be updated every time, because the view may be recycled
Header header = getItem(position);
if (mRemoveIconIfEmpty) {
if (header.iconRes == 0) {
holder.icon.setVisibility(View.GONE);
} else {
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(header.iconRes);
}
} else {
holder.icon.setImageResource(header.iconRes);
}
holder.title.setText(header.getTitle(getContext().getResources()));
CharSequence summary = header.getSummary(getContext().getResources());
if (!TextUtils.isEmpty(summary)) {
holder.summary.setVisibility(View.VISIBLE);
holder.summary.setText(summary);
} else {
holder.summary.setVisibility(View.GONE);
}
return view;
}
}
preference_header_item.xml with modified minHeight:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="64dp"
android:background="?android:attr/activatedBackgroundIndicator"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize"
android:paddingEnd="?android:attr/scrollbarSize">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dip"
android:layout_marginEnd="6dip"
android:layout_gravity="center" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dip"
android:layout_marginStart="2dip"
android:layout_marginRight="6dip"
android:layout_marginEnd="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView android:id="#+id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#android:id/title"
android:layout_alignLeft="#android:id/title"
android:layout_alignStart="#android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:ellipsize="end"
android:maxLines="2" />
</RelativeLayout>
</LinearLayout>

Related

Android listview lagging with complex layout

Like the title say, i create list view which each item display image, three line text, and a button, but the view is so lagging even in samsung galaxy mega dual core, here my item view xml
<RelativeLayout
android:gravity="center_vertical"
android:padding="5.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:listPreferredItemHeight"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="#+id/compositeListViewItemThumbnailImage"
android:layout_width="60.0dip"
android:layout_height="60.0dip"
android:src="#drawable/ic_launcher"
android:scaleType="fitCenter"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:contentDescription="#string/_empty_strings" />
<LinearLayout
android:orientation="vertical"
android:id="#+id/compositeListViewItemFirstLinearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/compositeListViewItemThumbnailImage"
android:layout_alignTop="#+id/compositeListViewItemThumbnailImage"
android:layout_alignBottom="#+id/compositeListViewItemThumbnailImage"
android:layout_alignParentRight="true">
<TextView
android:textSize="16.0sp"
android:gravity="center_vertical"
android:id="#+id/compositeListViewItemFirstLineText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world"
android:singleLine="true"
android:ellipsize="marquee"
android:lines="1"
android:inputType="textNoSuggestions" />
<TextView
android:textSize="12.0sp"
android:id="#+id/compositeListViewItemSecondLineText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world"
android:singleLine="true"
android:ellipsize="marquee"
android:lines="1"
android:inputType="textNoSuggestions" />
<TextView
android:textSize="14.0sp"
android:singleLine="true"
android:ellipsize="marquee"
android:lines="1"
android:layout_gravity="center_vertical"
android:id="#+id/compositeListViewItemThirdLineText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world"
android:inputType="textNoSuggestions" />
</LinearLayout>
<Button
android:id="#+id/compositeListViewItemActionButton"
android:paddingLeft="5.0dip"
android:paddingTop="5.0dip"
android:paddingRight="5.0dip"
android:paddingBottom="5.0dip"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="30.0dip"
android:layout_marginTop="30.0dip"
android:layout_marginRight="0.0dip"
android:text="#string/hello_world"
android:layout_alignParentRight="true"
style="?android:attr/buttonStyleSmall" />
</RelativeLayout>
as it might / might not be affected by the adapter view, i also put it here
public class SimpleAdapter extends ArrayAdapter implements Serializable {
private ArrayList<?> items;
private int itemLayout;
private String[] itemFieldsName;
private int[] itemElements;
private Context ctx;
public SimpleAdapter(Context ctx, ArrayList<?> items, int itemLayout, int[] itemElements, String[] itemFieldsName) {
super(ctx, itemLayout, items);
this.items = items;
this.itemElements = itemElements;
this.itemLayout = itemLayout;
this.itemFieldsName = itemFieldsName;
this.ctx = ctx;
}
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if (itemView == null) {
itemView = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(this.itemLayout, null);
}
Object item = this.items.get(position);
for (int i = 0; i < this.itemElements.length; i++) {
Object value = getValue(item, Character.toUpperCase(this.itemFieldsName[i].charAt(0)) + this.itemFieldsName[i].substring(1));
View elementView = null;
if (itemView != null) {
elementView = itemView.findViewById(this.itemElements[i]);
if ((elementView instanceof TextView)) {
((TextView) elementView).setText(value.toString());
} else if ((elementView instanceof SmartImageView)) {
((SmartImageView) elementView).setImageUrl(value.toString());
} else if ((elementView instanceof Button)) {
((Button) elementView).setText(value.toString());
}
}
}
return itemView;
}
private Object getValue(Object obj, String fieldName) {
ArrayList<String> fieldsName = new ArrayList<String>();
int length;
String str = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
Object value = new Object();
try {
value = obj.getClass().getMethod("get" + str, new Class[0]).invoke(obj);
} catch (Exception ex) {
value = fieldName;
}
return value;
}
private String trimString(String string, int length, boolean soft) {
if(string == null || string.trim().isEmpty()){
return string;
}
StringBuffer sb = new StringBuffer(string);
int actualLength = length - 3;
if(sb.length() > actualLength){
if(!soft)
return sb.insert(actualLength, "...").substring(0, actualLength+3);
else {
int endIndex = sb.indexOf(" ",actualLength);
return sb.insert(endIndex,"...").substring(0, endIndex+3);
}
}
return string;
}
}
Can someone tell me where i was wrong?,
tell me if you need more explanation / code resource
Finding an inner view inside an inflated layout is among the most common operations in Android. This is usually done through a View method called findViewById(). This method will recursively go through the view tree looking for a child with a given IDcode. Using findViewById() on static UI layouts is totally fine but, as you’ve seen, ListView calls the adapter’s getView() very frequently when scrolling. findViewById() might perceivably hit scrolling performance in ListViews—especially if your row layout is non-trivial.
The View Holder pattern is about reducing the number of findViewById() calls in the adapter’s getView(). In practice, the View Holder is a lightweight inner class that holds direct references to all inner views from a row. You store it as a tag in the row’s view after inflating it. This way you’ll only have to use findViewById() when you first create the layout. Here’s the code sample with View Holder pattern applied:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = convertView.getTag();
}
holder.text.setText("Position " + position);
return convertView;
}
private static class ViewHolder {
public TextView text;
}
There is one known issue with android:singleLine where it kills performance on ListViews drastically. Please see this question Why does android:singleLine="true" make ListView scrolling very laggy?
Remove it from your layout and use maxLines instead.

Personalized Listview error on set background color

i have a problem in my app:
i use a listview with a personalized adapter,
in this adapter i want to change the color of the line depending on whether the message is read or not.
In the metod GETVIEW i control a variable, if it is equal to 0 i want to change the background color.
All works and the list is displayed as i want,
but when there are a lot of elements and the list is scrolled in any direction (from top to bottom and vice versa) the raws are displeyed with the same color even if by code is set another color.
Has anyone ever had the same problem?
You can advise me something about it?
There is the code of my Adapter:
public class LazyAdapterComunicazioni extends BaseAdapter {
private Activity activity;
private String[] id;
private String[] titolo;
private String[] data;
private String[] letto;
private static LayoutInflater inflater=null;
//public ImageLoader imageLoader;
public LazyAdapterComunicazioni(Activity a, String[] idCom, String[] titoloCom, String[] dataCom, String[]lettoCom) {
activity = a;
id = idCom;
titolo = titoloCom;
data = dataCom;
letto = lettoCom;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return id.length;
}
#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) {
View vi = convertView;
if(convertView == null)
{
vi = inflater.inflate(R.layout.comunicazionicslist, null);
}
ContactsViewHolder viewHolder = new ContactsViewHolder();
//Settimane
viewHolder.txtTitolo=(TextView)vi.findViewById(R.id.comCS_Titolo);
viewHolder.txtTitolo.setText(titolo[position].toString());
//Data
viewHolder.txtData=(TextView)vi.findViewById(R.id.comCS_Data);
viewHolder.txtData.setText(data[position].toString());
//ID
viewHolder.txtID=(TextView)vi.findViewById(R.id.comCS_ID);
viewHolder.txtID.setText(id[position].toString());
//Connessianne e Apretura del DB
String read = letto[position].toString();
if (read.equals("0")) //DA LEGGERE
{
//LAYOUT
viewHolder.rel = (RelativeLayout)vi.findViewById(R.id.comCS_RIGA);
viewHolder.rel.setBackgroundResource(R.drawable.sfondorigacomcs);
viewHolder.txtTitolo.setTextColor(Color.WHITE);
}
return vi;
}
static class ContactsViewHolder {
TextView txtTitolo;
TextView txtData;
TextView txtID;
RelativeLayout rel;
}
}
and the xml of the single row:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/comCS_RIGA"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#ffffff"
android:paddingTop="10dp"
android:paddingBottom="10dp"
>
<TextView
android:id="#+id/comCS_Data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="#000000"
android:layout_alignParentLeft="true"
android:layout_marginLeft="16dp"
android:textSize="12sp"
/>
<TextView
android:id="#+id/comCS_ID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/comCS_Data"
android:visibility="invisible"
/>
<TextView
android:id="#+id/comCS_Titolo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/comCS_Data"
android:layout_marginTop="2dp"
android:layout_marginLeft="16dp"
android:text=""
android:textColor="#357cbc"
android:textSize="18sp"
/>
<ImageView
android:id="#+id/feedbackCars_Positivo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/frecciacom"
android:contentDescription="#string/title_Comunicazioni"
android:layout_alignParentRight="true"
android:layout_marginTop="2dp"
android:layout_marginRight="16dp"
/>
</RelativeLayout>
It's a common error.
Views are recycled so you have to set back default values in any cases.
if (read.equals("0")) //DA LEGGERE
{
//LAYOUT
viewHolder.rel = (RelativeLayout)vi.findViewById(R.id.comCS_RIGA);
viewHolder.rel.setBackgroundResource(R.drawable.sfondorigacomcs);
viewHolder.txtTitolo.setTextColor(Color.WHITE);
}
else {
viewHolder.rel.setBackgroundResource("you_defaulf_bg_res");
}
First of all, you are not using the ViewHolder pattern correctly. The ViewHolder is designed to hold references to the Views of the list-item and minimize the findViewById(...) calls.
That purpose is not fulfilled by your code. The reason for the behaviour you described lies in the Views not being recycled.
Do it like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ContactsViewHolder viewHolder;
if(convertView == null) {
vi = inflater.inflate(R.layout.comunicazionicslist, null);
viewHolder = new ContactsViewHolder();
viewHolder.txtTitolo=(TextView)vi.findViewById(R.id.comCS_Titolo);
viewHolder.txtData=(TextView)vi.findViewById(R.id.comCS_Data);
viewHolder.txtID=(TextView)vi.findViewById(R.id.comCS_ID);
viewHolder.rel = (RelativeLayout)vi.findViewById(R.id.comCS_RIGA);
vi.setTag(viewHolder);
} else {
viewHolder = (ContactsViewHolder) vi.getTag();
}
//Settimane
viewHolder.txtTitolo.setText(titolo[position].toString());
//Data
viewHolder.txtData.setText(data[position].toString());
//ID
viewHolder.txtID.setText(id[position].toString());
//Connessianne e Apretura del DB
String read = letto[position].toString();
if (read.equals("0")) //DA LEGGERE {
viewHolder.rel.setBackgroundResource(R.drawable.sfondorigacomcs);
viewHolder.txtTitolo.setTextColor(Color.WHITE);
} else {
viewHolder.rel.setBackgroundResource(R.drawable.NORMAL_BACKGROUND);
}
return vi;
}

Radio Group implementation on Grid View in Android

I want Radio Group implementation on GridView, so that only single item can be selected among the elements of grid.
Please help.
The purpose to restrict the select of element from the grid can be accomplished as follows:
1.Creation of Grid element.
<LinearLayout
android:id="#+id/item_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_launcher" />
<RadioButton
android:id="#+id/radiobtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Image" />
</LinearLayout>
2.Inflating this xml in the getView() method of customize adapter.
public class MyAdapter extends BaseAdapter {
Context mCtx;
int[] mImg;
LayoutInflater layoutInflater;
RadioGroup rgp;
private RadioButton mSelectedRB;
private int mSelectedPosition = -1;
public MyAdapter(Context context, int[] img) {
this.mCtx = context;
this.mImg = img;
rgp = new RadioGroup(context);
layoutInflater = (LayoutInflater) mCtx
.getSystemService(LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mImg.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView,
ViewGroup parent) {
View view = convertView;
Holder holder;
if (view == null) {
view = layoutInflater.inflate(R.layout.element, null);
holder = new Holder();
holder.image = (ImageView) view.findViewById(R.id.imageView);
holder.radioButton = (RadioButton) view
.findViewById(R.id.radiobtn);
view.setTag(holder);
} else {
holder = (Holder) view.getTag();
}
holder.radioButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if ((position != mSelectedPosition && mSelectedRB != null)) {
mSelectedRB.setChecked(false);
}
mSelectedPosition = position;
mSelectedRB = (RadioButton) v;
}
});
if (mSelectedPosition != position) {
holder.radioButton.setChecked(false);
} else {
holder.radioButton.setChecked(true);
if (mSelectedRB != null && holder.radioButton != mSelectedRB) {
mSelectedRB = holder.radioButton;
}
}
return view;
}
}
private class Holder {
ImageView image;
RadioButton radioButton;
}
An alternative approach to this is to create your own subclass of RadioButton which has an extra XML attribute (such as group). This specifies (as a string) to which group the button belongs. In the subclass, you then ensure that within any particular group, only one radio button is selected.
You can do this as follows:
First create the file res/values/attrs.xml which contains something like the following:
<resources>
<declare-styleable name="GroupedRadioButton">
<attr name="group" format="string"/>
</declare-styleable>
</resources>
Then create your subclass, GroupedRadioButton:
public class GroupedRadioButton extends RadioButton {
public GroupedRadioButton(Context context, AttributeSet attrs) {
super(context, attrs);
processAttributes(context, attrs);
setOnClickListener(internalListener, true);
}
...
}
Once fleshed out (see below), you can then use this new class as follows in your layout files:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.app"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.app.GroupedRadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Option 1"
custom:group="group1" />
<com.example.app.GroupedRadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Option 2"
custom:group="group1" />
<com.example.app.GroupedRadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Option 3"
custom:group="group1" />
<com.example.app.GroupedRadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Option A"
custom:group="group2" />
<com.example.app.GroupedRadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Option B"
custom:group="group2" />
...
The radio buttons can be anywhere in your layout (e.g. in a GridView). Note the xmlns:custom tag is required since we are using a custom attribute.
The layout above will make options 1, 2 and 3 mutually exclusive and options A and B mutually exclusive.
This is achieved by keeping track (statically) of which GroupedRadioButton is currently selected within each group:
public class GroupedRadioButton extends RadioButton {
private static Map<String, WeakReference<GroupedRadioButton>> buttonMap;
static {
buttonMap = new HashMap<String, WeakReference<GroupedRadioButton>>();
}
...
}
Note that we have to be careful here to ensure that we don't keep strong references to the buttons otherwise they will never be garbage collected.
The processAttributes() method specified in the constructor above digs out the group attribute from the XML we specified and sets this as instance data:
private void processAttributes(Context context, AttributeSet attrs) {
TypedArray attributes = context.obtainStyledAttributes(attrs,
R.styleable.GroupedRadioButton);
int attributeCount = attributes.getIndexCount();
for (int i = 0; i < attributeCount; ++i) {
int attr = attributes.getIndex(i);
switch (attr) {
case R.styleable.GroupedRadioButton_group:
this.groupName = attributes.getString(attr);
break;
}
}
attributes.recycle();
}
We define the main OnClickListener for this class.
private OnClickListener internalListener = new OnClickListener() {
#Override
public void onClick(View view) {
processButtonClick(view);
}
};
which calls:
private void processButtonClick(View view) {
if (!(view instanceof GroupedRadioButton))
return;
GroupedRadioButton clickedButton = (GroupedRadioButton) view;
String groupName = clickedButton.groupName;
WeakReference<GroupedRadioButton> selectedButtonReference = buttonMap.get(groupName);
GroupedRadioButton selectedButton = selectedButtonReference == null ? null : selectedButtonReference.get();
if (selectedButton != clickedButton) {
if (selectedButton != null)
selectedButton.setChecked(false);
clickedButton.setChecked(true);
buttonMap.put(groupName, new WeakReference<GroupedRadioButton>(clickedButton));
}
if (externalListener != null)
externalListener.onClick(view);
}
This does two things. It ensures that it deselects the old group button before selecting the new one (assuming the old and new buttons are different). It then calls onClick() on an externalListener which is provided so that users of the class can add their own 'on-click' functionality.
The setOnClickListener() call in the constructor is to our own method as follows:
private void setOnClickListener(OnClickListener listener, boolean internal) {
if (internal)
super.setOnClickListener(internalListener);
else
this.externalListener = listener;
}
This sets the internalListener as the official OnClickListener and sets instance data as appropriate for the external listener. The View.setOnClickListener() method can then be overridden as follows:
#Override
public void setOnClickListener(OnClickListener listener) {
setOnClickListener(listener, false);
}
Apologies for the length of this answer but I hope it helps you and others trying to do the same thing. It would of course not be needed at all if a RadioGroup applied recursively to its children!
When you select an element of your grid, check that none of the other elements that you want to be in your radio group aren't selected and if they are deselect them...

The visibility of the items change when scrolled ListView

I am using a list view and an adapter for loading a list,each list item has a TextView,EditText and Image..I set the visibility of the arrow and the Edit text according to the position of the list row,everything works fine when I load the list for the first time...
But when I scroll through the list,visibility of the items keep changing...Kindly help me in this issue...The relevant codes has been attached...
<?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:background="#FFFFFF">
<TextView android:layout_height="wrap_content" android:layout_width="0dip"
android:textSize="20dip" android:layout_weight="1"
android:id="#+id/textview_add_lot_list" android:textColor="#android:color/black"
android:paddingTop="10dip" android:paddingBottom="10dip"
android:paddingLeft="10dip"/>
<EditText android:layout_height="fill_parent" android:layout_width="0dip"
android:layout_weight="1" android:id="#+id/et_add_lot_list"
android:layout_gravity="center_vertical"/>
<ImageView android:layout_height="wrap_content" android:layout_width="wrap_content"
android:id="#+id/imageview_arrow_add_lot_list" android:layout_gravity="center_vertical"
android:visibility="invisible" android:src="#drawable/more_reviews_arrow"
android:paddingRight="10dip"/>
</LinearLayout>
Java code activity...
final ArrayList<String> listItems = new ArrayList<String>();
listItems.add("Parking name");
listItems.add("Address");
listItems.add("City");
listItems.add("State");
listItems.add("Zip");
listItems.add("Phone");
listItems.add("Web Address");
listItems.add(" ");
listItems.add("Parking Image");
listItems.add(" ");
listItems.add("Open Hours");
listItems.add(" ");
listItems.add("Web Reviews");
final AddParkingLotAdapter adapter = new AddParkingLotAdapter(mAppContext,0,listItems);
lv.setAdapter(adapter);
Java code...adapter
public class AddParkingLotAdapter extends ArrayAdapter<String> {
private ArrayList<String> mStrings;
private LayoutInflater mInflater;
private AppContext mContext;
private static int NON_EMPTY_ROW = 1;
private static int EMPTY_ROW = 0;
public AddParkingLotAdapter(Context context, int resId, List<String> strings) {
super(context, resId,strings);
mStrings = (ArrayList<String>) strings;
mContext = (AppContext) context;
mInflater = LayoutInflater.from(context);
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getCount() {
return mStrings.size();
}
#Override
public String getItem(int position) {
return mStrings.get(position);
}
#Override
public int getItemViewType(int position) {
if(position==7||position==9||position==11){
return EMPTY_ROW;
}else{
return NON_EMPTY_ROW;
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView itemTextView = null;
//different inflations for different type rows..
if(getItemViewType(position) == EMPTY_ROW){
if (convertView == null) {
convertView = mInflater.inflate(R.layout.review_empty_row, null);
}
}else if(getItemViewType(position) == NON_EMPTY_ROW){
if (convertView == null) {
convertView = mInflater.inflate(R.layout.add_parkinglist_item, null);
}
itemTextView = (TextView) convertView.findViewById(R.id.textview_add_lot_list);
itemTextView.setText(mStrings.get(position));
if (position==3||position==8||position==10||position==12){
ImageView itemImageView = (ImageView)convertView.findViewById(R.id.imageview_arrow_add_lot_list);
itemImageView.setVisibility(View.VISIBLE);
EditText editText = (EditText)convertView.findViewById(R.id.et_add_lot_list);
editText.setVisibility(View.INVISIBLE);
}
}
return convertView;
}
}
In this code:
if (position==3||position==8||position==10||position==12){
ImageView itemImageView = (ImageView)convertView.findViewById(R.id.imageview_arrow_add_lot_list);
itemImageView.setVisibility(View.VISIBLE);
EditText editText = (EditText)convertView.findViewById(R.id.et_add_lot_list);
editText.setVisibility(View.INVISIBLE);
}
you've got no else clause. That means that if position is 0,1,2,4,5 or 6 you don't explicitly set the visibility of the views and so the visibility will be whatever it was set to when the views were recycled. If convertView is non-null, you always need to reset the visibility of any items whose visibility may be been modified earlier.

Android ListView throwing NullPointerException when I try to scroll

Not exactly sure what is going on here so I am hoping one of you can point me in the right direction. I have a ListView within a Tab frame and for some reason, it loads but crashes when I try to scroll.
Here is all of the information I could gather
Here is the Activity that goes into the frame... it parses an XML file into a priority queue, gets the attached intent information, and loads the desired page.
public class QScoresFrameActivity extends ListActivity
{
private PriorityQueue<Score> mScores;
private int mDifficulty;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.scoresframe);
mDifficulty = getIntent().getIntExtra(getResources().getString(R.string.difficulty), getResources().getInteger(R.integer.normal_level));
mScores = QGameGlobals.ParseScoresXML(this,mDifficulty);
setListAdapter(new ScoresAdapter(this));
}
private class ScoresAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
public ScoresAdapter(Context context)
{
mInflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent)
{
Score thisScore = mScores.getByIndex(position);
convertView = mInflater.inflate(R.layout.scoreview, null);
TextView posT;
TextView nameT;
TextView scoreT;
String name = thisScore.getName();
int score = thisScore.getScore();
posT = (TextView)convertView.findViewById(R.id.position);
nameT = (TextView)convertView.findViewById(R.id.scorerName);
scoreT= (TextView)convertView.findViewById(R.id.scorerScore);
posT.setText(String.valueOf(position + 1) + ". ");
nameT.setText(name);
scoreT.setText(String.valueOf(score));
return convertView;
}
public int getCount()
{
return mScores.getLength();
}
public Object getItem(int position)
{
return this;
}
public long getItemId(int position)
{
return position;
}
}
}
The layout file that holds the list:
<?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" android:orientation="vertical">
<ListView android:layout_height="wrap_content" android:layout_width="match_parent" android:id="#+android:id/list" />
</LinearLayout>
The layout file for each item in the list:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="#+id/linearLayout1" android:padding="10dp" android:layout_width="fill_parent" android:baselineAligned="false" android:orientation="horizontal" android:layout_height="wrap_content">
<TextView android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="TextView" android:paddingRight="10dp" android:id="#+id/position" android:layout_width="50dp" android:textColor="#color/teal" ></TextView>
<TextView android:id="#+id/scorerName" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="TextView" android:layout_width="wrap_content" android:layout_gravity="center_vertical"></TextView>
<TextView android:id="#+id/scorerScore" android:text="TextView" android:textAppearance="?android:attr/textAppearanceLarge" android:paddingLeft="20dp" android:layout_height="wrap_content" android:gravity="right" android:textColor="#color/gold" android:layout_width="match_parent" android:layout_gravity="center_vertical"/>
</LinearLayout>
Priority Queue class....
#SuppressWarnings("rawtypes")
public class PriorityQueue<T extends Comparable> extends Queue<T>
{
public PriorityQueue ()
{
super();
}
#SuppressWarnings("unchecked")
#Override
public void enqueue(T item)
/* Enqueue a new object of type T into the queue based on its compareTo method.
* Sorts from greatest to least.
*/
{
TNode newNode = new TNode(item);
if (root == null)
{
root = newNode;
end = newNode;
}
else
{
TNode currNode = root;
TNode lastNode = root;
while (true)
{
int compVal = item.compareTo(currNode.get());
if (compVal == 1 || compVal == 0)
{
if (currNode == root)
{
root = newNode;
}
else
{
lastNode.next = newNode;
newNode.next = currNode;
}
newNode.next = currNode;
break;
}
else
{
if (currNode == end)
{
currNode.next = newNode;
end = newNode;
break;
}
else
{
lastNode = currNode;
currNode = currNode.next;
}
}
}
}
length++;
}
}
Appreciate the help.
A stack trace here would be most helpful. However, offhand, I would guess that the problem is you are storing the inflator in the adapter's constructor. Instead, just call getLayoutInflator() inside your getView() method. If that doesn't resolve it, please do post the logcat.
Just gonna guess because I ran into this when I started out with Android...
I know the documentation says to name your list android:id="#+android:id/list" but try android:id="#id/android:list" instead also if it just so happens that you're starting with no info (empty list) define a view with android:id="#id/android:empty" as a placeholder.
The problem is with your getView() function in your custom listadapter (ScoreAdapter). When you scroll, the listview recycles your views and calls the getView() function. Change you getView() function to this to fix it:
public View getView(int position, View convertView, ViewGroup parent)
{
Score thisScore = mScores.getByIndex(position);
if (convertView == null)
{
LayoutInflater mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.scoreview, null);
}
TextView posT;
TextView nameT;
TextView scoreT;
String name = thisScore.getName();
int score = thisScore.getScore();
posT = (TextView)convertView.findViewById(R.id.position);
nameT = (TextView)convertView.findViewById(R.id.scorerName);
scoreT= (TextView)convertView.findViewById(R.id.scorerScore);
posT.setText(String.valueOf(position + 1) + ". ");
nameT.setText(name);
scoreT.setText(String.valueOf(score));
return convertView;
}
Ahh yes, your problem is definitely in the PriorityQueue. That enqueue method is all sorts of broken, sorry. Get out a pencil and paper and walk through the scenarios of adding in the middle of the queue. I'm pretty certain that I see at least one bug, and possibly as many as three.
--randy

Categories

Resources