简体   繁体   中英

Dynamic buttons added to a GridLayout using RecyclerView are added on top of each other

I want to add dynamic buttons to a grid layout as shown below. Each button is added one at a time , and the layout must update itself to have the buttons in rows. My problem is that when a new button is added it appears on top of the last button added, and is not placed in a row format. In this image below I show what I want and what is currently happening, where the squares labeled with numbers are my buttons. In the image of what I have, I have added 6 buttons to the layout, but they are all on top of each other.

在此处输入图片说明

I have looked around about how to do this, and it was recommended to me that I use a RecyclerView with a GridLayoutManager . I have added this to my code, but like I said the problem is that when I add a button, and then if I add another button, the second one is added on top of the first. What I have works if my buttons are preset, but not for dynamic buttons.

Here is my code:

Main Fragment initiates the RecyclerView . I have another activity that initiates createButton() method and passes a drawable and String . These drawables and strings are passed to this method one at a time based on the user's action, and create an image button one at a time.

public class MyFragment extends Fragment {

    private GridLayoutManager lLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    // onCreateView
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.my_fragment, container, false);

        // Get screen size to have different layouts for phone and tablet
        int screenSize = getResources().getConfiguration().screenLayout &
                Configuration.SCREENLAYOUT_SIZE_MASK;

        String toastMsg;
        switch (screenSize) {
            case Configuration.SCREENLAYOUT_SIZE_LARGE:
                toastMsg = "Large screen";
                Log.d("tag_name", "Large screen");
                break;
            case Configuration.SCREENLAYOUT_SIZE_NORMAL:
                toastMsg = "Normal screen";
                Log.d("tag_name", "Normal screen");
                break;
            case Configuration.SCREENLAYOUT_SIZE_SMALL:
                toastMsg = "Small screen";
                Log.d("tag_name", "Small screen");
                break;
            default:
                toastMsg = "Screen size is neither large, normal or small";
                Log.d("tag_name", "Screen size is not large, normal, or small");
        }
        Toast.makeText(getActivity(), toastMsg, Toast.LENGTH_LONG).show();

        // Create an empty list to initialize the adapter (or else get nullPointerException error)
        List<ItemObject> myList = new ArrayList<ItemObject>();

        // 3 rows for tablet
        // 2 rows for phone

        if (screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE
                || screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
            lLayout = new GridLayoutManager(getActivity(), 3, GridLayoutManager.HORIZONTAL, false);
        } else
            lLayout = new GridLayoutManager(getActivity(), 2, GridLayoutManager.HORIZONTAL, false);

        RecyclerView rView = (RecyclerView) view.findViewById(R.id.recycler_view);

        rView.setHasFixedSize(true);
        rView.setLayoutManager(lLayout);

        RecyclerViewAdapter rcAdapter = new RecyclerViewAdapter(getActivity(), myList);
        rView.setAdapter(rcAdapter);

        return view;
    }

    private List<ItemObject> getAllItemList(String applicationName, Drawable app_drawable) {

        List<ItemObject> allItems = new ArrayList<ItemObject>();
        allItems.add(new ItemObject(applicationName, app_drawable));

        return allItems;
    }

    public void createButton(Drawable d, String appName) {

        List<ItemObject> rowListItem = getAllItemList(appName, d);
        lLayout = new GridLayoutManager(getActivity(), 2, GridLayoutManager.HORIZONTAL, false);


        RecyclerView rView = (RecyclerView) getView().findViewById(R.id.recycler_view);
        rView.setHasFixedSize(true);
        rView.setLayoutManager(lLayout);

        RecyclerViewAdapter rcAdapter = new RecyclerViewAdapter(getActivity(), rowListItem);
        rView.setAdapter(rcAdapter);

    }

}

My layout (my_fragment):

<LinearLayout android:id="@+id/my_fragment"
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_marginTop="15dp"
          android:gravity="center_horizontal">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scrollbars="horizontal"/>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageButton
            android:id="@+id/new_app_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <TextView
            android:id="@+id/new_app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/new_app_button"
            android:gravity="center"/>

    </RelativeLayout>
</LinearLayout>

EDIT

HERE IS MY UPDATED CODE FOR MyFragment Now I am getting a nullPointerException when the second ImageButton gets added

public class MyFragment extends Fragment {

private GridLayoutManager lLayout;

private ButtonCreator bc;
RecyclerViewAdapter rcAdapter;


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    List<ItemObject> myList = new ArrayList<ItemObject>();
    rcAdapter = new RecyclerViewAdapter(getActivity(),myList);

}

// onCreateView
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.home_fragment, container, false);


    // Get screen size so we can have different layouts for phone and tablet
    int screenSize = getResources().getConfiguration().screenLayout &
            Configuration.SCREENLAYOUT_SIZE_MASK;

    String toastMsg;
    switch(screenSize) {
        case Configuration.SCREENLAYOUT_SIZE_LARGE:
            toastMsg = "Large screen";
            Log.d("tag_name", "Large screen");
            break;
        case Configuration.SCREENLAYOUT_SIZE_NORMAL:
            toastMsg = "Normal screen";
            Log.d("tag_name", "Normal screen");
            break;
        case Configuration.SCREENLAYOUT_SIZE_SMALL:
            toastMsg = "Small screen";
            Log.d("tag_name", "Small screen");
            break;
        default:
            toastMsg = "Screen size is neither large, normal or small";
            Log.d("tag_name", "Screen size is not large, normal, or small");
    }
    Toast.makeText(getActivity(), toastMsg, Toast.LENGTH_LONG).show();


    // Create an empty list to initialize the adapter (or else get nullPointerException error)

    if (screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE
    || screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
        lLayout = new GridLayoutManager(getActivity(), 3, GridLayoutManager.HORIZONTAL, false);
    }

    else lLayout = new GridLayoutManager(getActivity(), 2, GridLayoutManager.HORIZONTAL, false);


    RecyclerView rView = (RecyclerView)view.findViewById(R.id.recycler_view);

    rView.setHasFixedSize(true);
    rView.setLayoutManager(lLayout);


    rView.setAdapter(rcAdapter);


    return view;
}

private List<ItemObject> getAllItemList(String applicationName, Drawable app_drawable){

    List<ItemObject> allItems = new ArrayList<ItemObject>();
    allItems.add(new ItemObject(applicationName, app_drawable));


    return allItems;
}


public void createButton (Drawable d, String appName){

    rcAdapter.addItem(new ItemObject(appName , d));

}



// In order to get the view of HomeFragment correctly

public interface ButtonCreator{
    void buttonCreator(Drawable d, String string);
}



@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    bc = (ButtonCreator) activity;
}

}

How I am calling createButton method in MyFragment (called in the runOnUiThread method)

public class MainActivity extends FragmentActivity implements MyFragment.ButtonCreator {


private Thread repeatTaskThread;
private byte[] byteArray;

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

   FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
   ft.replace(R.id.container, new MyFragment()).commit();
}


public void buttonCreator(Drawable d,String a) {
    MyFragment homeFragment = (MyFragment)getSupportFragmentManager().getFragments().get(1);
    homeFragment.createButton(d, a);
}

public void RepeatTask(){

     repeatTaskThread = new Thread() {
        public void run() {
           while (true) {


              try {


                 System.out.println("TRY_1");

                 Socket socket = new Socket("192.168.0.26", 5050);

                 // Get data sent through socket
                 DataInputStream DIS = new DataInputStream(socket.getInputStream());

                 System.out.println("DataInputStream Started");

                 // read data that got sent
                 final String applicationName = DIS.readUTF();


                 // read array data for bitmap

                 int len = DIS.readInt();
                 byte[] data = new byte[len];

                 DIS.readFully(data, 0, data.length);


                 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                 final Drawable d = new BitmapDrawable(getResources(), bitmap);


                  runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                       // TODO Auto-generated method stub


                       Log.d("tag_name", "Try_2");

                        buttonCreator(d,applicationName);


                    }
                 });


                 socket.close();




              } catch (Exception e) {


                 System.out.println("Exception is " + e.toString());

              }


              try {
                 // Sleep for 5 seconds
                 Thread.sleep(5000);
              } catch (Exception e) {
                 e.printStackTrace();
              }
           }
        }

        ;
     };
     repeatTaskThread.start();


}

}

MY error when my app crashes on the second button creation:

05-11 17:05:25.275 14257-14257/it.anddev.bradipao.janus E/RecyclerView: No adapter attached; skipping layout
05-11 17:05:25.275 14257-14257/it.anddev.bradipao.janus D/AndroidRuntime: Shutting down VM
05-11 17:05:25.275 14257-14257/it.anddev.bradipao.janus W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x437c0160)
05-11 17:05:25.305 14257-14257/it.anddev.bradipao.janus E/AndroidRuntime: FATAL EXCEPTION: main
                                                                      Process: it.anddev.bradipao.janus, PID: 14257
                                                                      java.lang.NullPointerException
                                                                          at android.support.v7.widget.RecyclerView.computeHorizontalScrollRange(RecyclerView.java:1518)
                                                                          at android.view.View.onDrawScrollBars(View.java:12248)
                                                                          at android.view.View.draw(View.java:14794)
                                                                          at android.support.v7.widget.RecyclerView.draw(RecyclerView.java:3171)
                                                                          at android.view.View.getDisplayList(View.java:13655)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.View.draw(View.java:14505)
                                                                          at android.view.ViewGroup.drawChild(ViewGroup.java:3134)
                                                                          at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2971)
                                                                          at android.view.View.getDisplayList(View.java:13650)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.View.draw(View.java:14505)
                                                                          at android.view.ViewGroup.drawChild(ViewGroup.java:3134)
                                                                          at android.support.v7.widget.RecyclerView.drawChild(RecyclerView.java:3693)
                                                                          at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2971)
                                                                          at android.view.View.draw(View.java:14791)
                                                                          at android.support.v7.widget.RecyclerView.draw(RecyclerView.java:3171)
                                                                          at android.view.View.getDisplayList(View.java:13655)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3108)
                                                                          at android.view.View.getDisplayList(View.java:13593)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3108)
                                                                          at android.view.View.getDisplayList(View.java:13593)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3108)
                                                                          at android.view.View.getDisplayList(View.java:13593)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3108)
                                                                          at android.view.View.getDisplayList(View.java:13593)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3108)
                                                                          at android.view.View.getDisplayList(View.java:13593)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3108)
                                                                          at android.view.View.getDisplayList(View.java:13593)
                                                                          at android.view.View.getDisplayList(View.java:13697)
                                                                          at android.view.HardwareRenderer$GlRenderer.buildDisplayList(HardwareRenderer.java:1570)
                                                                          at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1449)
                                                                          at android.view.ViewRootImpl.draw(ViewRootImpl.java:2476)
                                                                          at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2300)
                                                                          at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1929)
                                                                          at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1043)
                                                                          at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885)
                                                                          at android.view.Choreographer$CallbackRecord.run(Choreographer.java:771)
                                                                          at android.view.Choreographer.doCallbacks(Choreographer.java:574)
                                                                          at android.view.Choreographer.doFrame(Choreographer.java:544)
                                                                          at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:757)
                                                                          at android.os.Handler.handleCallback(Handler.java:733)
                                                                          at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                          at android.os.Looper.loop(Looper.java:149)
                                                                          at android.app.ActivityThread.main(ActivityThread.java:5257)
                                                                          at java.lang.reflect.Method.invokeNative(Native Method)
                                                                          at java.lang.reflect.Method.invoke(Method.java:515)
                                                                          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
                                                                          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:604)
                                                                          at dalvik.system.NativeStart.main(Native Method)

Do something like this

public class MyFragment extends Fragment {

    private GridLayoutManager lLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    // onCreateView
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.my_fragment, container, false);

        // Get screen size to have different layouts for phone and tablet
        int screenSize = getResources().getConfiguration().screenLayout &
                Configuration.SCREENLAYOUT_SIZE_MASK;

        String toastMsg;
        switch (screenSize) {
            case Configuration.SCREENLAYOUT_SIZE_LARGE:
                toastMsg = "Large screen";
                Log.d("tag_name", "Large screen");
                break;
            case Configuration.SCREENLAYOUT_SIZE_NORMAL:
                toastMsg = "Normal screen";
                Log.d("tag_name", "Normal screen");
                break;
            case Configuration.SCREENLAYOUT_SIZE_SMALL:
                toastMsg = "Small screen";
                Log.d("tag_name", "Small screen");
                break;
            default:
                toastMsg = "Screen size is neither large, normal or small";
                Log.d("tag_name", "Screen size is not large, normal, or small");
        }
        Toast.makeText(getActivity(), toastMsg, Toast.LENGTH_LONG).show();

        // Create an empty list to initialize the adapter (or else get nullPointerException error)

        // 3 rows for tablet
        // 2 rows for phone

        if (screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE
                || screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
            lLayout = new GridLayoutManager(getActivity(), 3, GridLayoutManager.HORIZONTAL, false);
        } else
            lLayout = new GridLayoutManager(getActivity(), 2, GridLayoutManager.HORIZONTAL, false);

        RecyclerView rView = (RecyclerView) view.findViewById(R.id.recycler_view);

        rView.setHasFixedSize(true);
        rView.setLayoutManager(lLayout);

        rView.setAdapter(rcAdapter);

        return view;
    }

    RecyclerViewAdapter rcAdapter;

    private List<ItemObject> getAllItemList(String applicationName, Drawable app_drawable) {

        List<ItemObject> allItems = new ArrayList<ItemObject>();
        allItems.add(new ItemObject(applicationName, app_drawable));

        return allItems;
    }

    public void createButton(Drawable d, String appName) {

        rcAdapter.addItem(new ItemObject(appName , d))

    }

}

And in the adapter add this method

public void addItem(int position) {
        list.add(position);
        notifyItemInserted(position);
        notifyItemRangeChanged(position, list.size());
    }

EDIT

To prevent the warning try moving your adapter initialization to onCreate

@Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            List<ItemObject> myList = new ArrayList<ItemObject>();
            rcAdapter = new RecyclerViewAdapter(getActivity(), myList);
        }

When you create a button, you should only have to do two things:

  1. Update the data structure that is backing the RecyclerView adapter
  2. Notify the adapter that the data has changed using one of the notify...() methods of the adapter (there are several of them , use the most applicable one)

Your current code does not appear to do either of these things. You also do not need to create or set any new adapters or layout managers.

In your case, you would be adding an element to the list held by your adapter class and then probably call notifyItemInserted() with an appropriate position. You could do both at once by adding a method to your adapter:

public void addItem(ItemObject item) {
    mList.add(item);
    notifyItemInserted(mList.size() - 1);
}

Unrelated: On Android you can use the resource resolution system to determine the number of columns to show.

Make src/main/res/values/integers.xml and add:

<resources>
    <integer name="grid_column_count">2</integer>
</resources>

Make src/main/res/values-large/integers.xml and add:

<resources>
    <integer name="grid_column_count">3</integer>
</resources>

Notice the -large , which will inform the system that screens of this size and above should use this alternate resource value. Now in your Fragment you can simply write:

int columnCount = getResources().getInteger(R.integer.grid_column_count);
GridLayoutManager layout = new GridLayoutManager(getActivity(), columnCount, 
        GridLayoutManager.HORIZONTAL, false);

I mention this because screen size bucket (the -large ) may not be the best way to determine the number of columns. You might be better suited using a screen width qualifier instead (such as -w600dp ), which will be updated if the device rotates (whereas the size bucket does not change in that scenario). You can read more about resource qualifiers here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM