2

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>
margincall
  • 483
  • 1
  • 6
  • 24

4 Answers4

4

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".

jmodrako
  • 334
  • 2
  • 6
3
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.

Krunal Shah
  • 1,438
  • 1
  • 17
  • 29
1

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.

jonstaff
  • 2,672
  • 2
  • 19
  • 18
  • Thank you for your answer. I re-coded with ViewHolder pattern, and yes, the pattern was nice and helpful. But my problem still remains. (I already used ArrayList for each item, but I just deleted from attached code, because of easy-reading.) The first checkBox is still uncheckable, but others works, really. I can get the state from isChecked and I'm using the result for other mechanism... – margincall Dec 04 '13 at 06:26
0

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.

Brissles
  • 3,833
  • 23
  • 31
  • **don't recycling** is the best way? I'm using the code that creating new grid component everytime, but just in this adapter. Other adapters check the null and decide create or not. It mind bothering me.. – margincall Jan 28 '14 at 05:42