1

Writing an Android app in Android Studio, I've got a GridView to display a set of images / text and I'm having trouble working out how to access a specific item within the list.

If I tap on an item, I can do things to it (in the code below I change the image displayed), but I can't figure out how to access a particular item (eg to change the image displayed) programmatically.

Initially I used an ImageAdapter from an example, but then I worked out if I changed it to use a TextView I could assign text to each as well as an image and it would all work well with Talkback (the Android screen reader).

I also initially added the OnTouchListener to the activity but I don't think I need that now as I just need to implement it in the adapter.

Finally I'm a bit confused on exactly what I need to be trying to access here - am I trying to get the item out of the GridView, or out of the ImageAdapter? I think the GridView sets the layout and defines where to place each item in the set and the ImageAdapter is a container for an array of items that defines the properties of each item in the set, is that right?

Kind regards

Quentin.

public class multitap extends Activity implements View.OnTouchListener {

    private GridView myGridView;
    public int numRows;
    public int numCols;
    public int currentChoice;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multitap);
        if (savedInstanceState == null) {
            Bundle extras = getIntent().getExtras();
            if (extras == null) {
                numRows = 1;
                numCols = 1;
            } else {
                numRows = extras.getInt("NUMBER_OF_GAME_ROWS");
                numCols = extras.getInt("NUMBER_OF_GAME_COLS");
            }
        } else {
            numRows = (int) savedInstanceState.getSerializable("NUMBER_OF_GAME_ROWS");
            numCols = (int) savedInstanceState.getSerializable("NUMBER_OF_GAME_COLS");
        }

        myGridView = (GridView)
                findViewById(R.id.myGridView);
        myGridView.setNumColumns(numCols);
        myGridView.setAdapter(new ImageAdapter(this));

        // now that everything is initialised, choose a random image tile.
        Random r = new Random();
        choice = r.nextInt(numCols*numRows);

    // This is the main thing I can't figure out - what do I need to replace "DoSomethingToFindItem" with?
    chosenItem = DoSomethingToFindItem(choice);
        // change the image of the chosen tile.
        chosenItem.setBackgroundResource(R.drawable.newImage);


    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        // turns out I don't need this as onTouch in adapter collects touch event
        return false;

    }


    public class ImageAdapter extends BaseAdapter {
        private Context mContext;

    //not really sure what mConext is?
        public ImageAdapter(Context c) {
            mContext = c;
        }

        public int getCount() { return numCols*numRows; }

        public Object getItem(int position) {
        // Is this right? All of the examples on the net use "return null" claiming they it's not needed (for their example)
            return this;
        }

        public long getItemId(int position) {
            // I know I should return something other than position here, but don't know what?
            // again every example doesn't feel the need for this function.
            return position;
        }

        // create a new TextView for each item referenced by the Adapter
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView textView;

            if (convertView == null) {  // if it's not recycled, initialize some attributes
                textView = new TextView(mContext);

            } else {
                textView = (TextView) convertView;
            }

            textView.setBackgroundResource(R.drawable.blankcell);
            textView.setText("" + (position + 1));
            textView.setTextColor(getResources().getColor(android.R.color.transparent));
            textView.setId(position);
            textView.setMaxHeight(parent.getHeight() / numRows);
            textView.setMinimumHeight(parent.getHeight() / numRows);


            textView.setOnTouchListener(new View.OnTouchListener() {
                public boolean onTouch(View v, MotionEvent event) {
                    int position;
                    position = v.getId();

                    // looking to get the first touch of any finger - this part works.
                    if ((event.getAction() == MotionEvent.ACTION_DOWN) || (event.getAction() == MotionEvent.ACTION_POINTER_DOWN)) {
                        TextView textView;
                        if (v == null) {  // if it's not recycled, initialize some attributes
                            textView = new TextView(mContext);

                        } else {
                            textView = (TextView) v;
                        }

                        // I can set a specific image by clicking on it.
                        textView.setBackgroundResource(R.drawable.clickedImage);

                        return true;
                    } else return false;
                }

                ;});
            return textView;
        }

    }
}
Rami
  • 7,879
  • 12
  • 36
  • 66
Quentin
  • 49
  • 1
  • 7
  • Try this answer: http://stackoverflow.com/a/5977058/4224337 – Rami Jan 31 '15 at 09:17
  • Thanks, I tried that and get a null pointer exception. I tried adding a toast message indicating the value of numVisibleChildren and firstVisiblePosition, and they both return 0 which I wasn't expecting - I've initialised everything and if I don't include this block of code, nothing else is called but the views are then visible on screen - is it somehow a matter of this code being run before the previous initialisation is finished and everything is drawn? – Quentin Jan 31 '15 at 09:43
  • Solved your problem? – Rami Jan 31 '15 at 09:44
  • Sorry - I discovered I couldn't press ENTER when writing my reply - see edit on my reply :) – Quentin Jan 31 '15 at 09:48
  • Where you store the list of GridView objects? – Rami Jan 31 '15 at 10:07
  • Ok just tried again but this time put the code in the onTouch function of the adapter - so it had to wait until I tapped on screen once, and it worked then. Is there a way to check / wait until the view is visible? – Quentin Jan 31 '15 at 10:09
  • the objects in the Grid View are PNG images in the res folder. – Quentin Jan 31 '15 at 10:12
  • No, the code shouldn't be inside the adapter, it will slow down the view. Are you using the same PNG image for all the items of the gridView? Otherwise from where you get the image of each item ( e.g you have an ArrayList of png files...). – Rami Jan 31 '15 at 10:24
  • The code setting the images initially is in the adapter's getView() function, initially it uses the same image for all of the items: "textView.setBackgroundResource(R.drawable.blankcell);", and then my intention is to use both code and touches later to change specific cells. Otherwise how should I initialise the grid view items with that backgroundResource etc? – Quentin Jan 31 '15 at 10:50
  • The best way is to store your images in an Object(List, ArrayList...) and then pass this object to the adapter. Later it will help you to manage your data(images). Take a look at this example: http://javatechig.com/android/android-gridview-example-building-image-gallery-in-android – Rami Jan 31 '15 at 10:56

1 Answers1

0

Ok, as is so often the case, there were several issues I was facing originally. One was accesing the items in the GridView, which Rami's answer (from the answer at: How to find element inside a gridview in Android? ) was correct for:

// newChoice is an int set to something in the range 0...myGridView.getChildCount()

v = (TextView) myGridView.getChildAt(newChoice);
v.setBackgroundResource(themeImages[ACTIVE1]);

The second issue I didn't realise at first, was that my logic in the information I was trying to get from the views would have worked once the views were drawn, but because this code managed to get executed before the views were all finished drawing, the values were not what I expected which meant heights of items marked visible were 0 etc. I found this code helped: getWidth() and getHeight() of View returns 0

final View theParent = parent;
final TextView theText = textView;
textView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        theText.post(new Runnable() {
            public void run() {
                // Now height is ready
                theText.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, (theParent.getHeight() / numRows)));
            }
        });
    }
});

Thanks Rami for your help and advice! (I also changed referencing the image files directly to using an array too as you suggested).

Community
  • 1
  • 1
Quentin
  • 49
  • 1
  • 7