Add Icons to Android Navigation Drawer?

2019-02-04 09:12发布

问题:

I have scoured the web for an answer to this question but cannot seem to find one, even in the official Android documentation.

I am in the process of creating an app that implements Android's new Navigation Drawer layout. Documentation of this layout can be found here and here.

In the design documentation as well as in a number of popular apps such as Facebook and Google Currents, the drawer items have icons in front of them.

The design documentation itself mentions them, under "Content of the Navigation Drawer"

Navigation targets can have optional leading icons as well as trailing counters. Use the counters to inform users about a changed state of data in the corresponding view.

Unfortunately, the Developer documentation makes no mention of how to implement icons in this setup. It describes how to implement the list of items as so:

public class MainActivity extends Activity {
private String[] mPlanetTitles;
private ListView mDrawerList;
...

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

    mPlanetTitles = getResources().getStringArray(R.array.planets_array);
    mDrawerList = (ListView) findViewById(R.id.left_drawer);

    // Set the adapter for the list view
    mDrawerList.setAdapter(new ArrayAdapter<String>(this,
            R.layout.drawer_list_item, mPlanetTitles));
    // Set the list's click listener
    mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

    ...
}
}

Because their implementation uses getStringArray(), there is no way I can think of to modify the row item's xml to include an icon.

This is baffling, because many apps seem to implement icons before each text item just fine, and the Design documentation even makes reference to adding icons. I feel that there must be some simple API call to accomplish adding these icons, and yet the only implementation I have found so far seems ridiculously complicated.

Does anyone know how to add icons to the Navigation Drawer items? Is there a way to do this with a simple API call, or have all these companies (Facebook, Google, etc) had to find ways to hack around the problem?

Thanks everyone.

回答1:

Because their implementation uses getStringArray(), there is no way I can think of to modify the row item's xml to include an icon.

The choice of model data for a ListAdapter has no impact whatsoever on whether the rows created by that ListAdapter can have icons.

Does anyone know how to add icons to the Navigation Drawer items?

Put an ImageView in your row layout for the ListAdapter. Populate that ImageView via your ListAdapter, such as by overriding getView() on your ArrayAdapter. IOW, you do it the same way as you do for any other ListView, as has been done for years.

Is there a way to do this with a simple API call

That depends on your definitions of "simple" and "API call".



回答2:

Create an Adapter Class like..

public class AdapterClass  extends ArrayAdapter<String> {
Context context;
private ArrayList<String> TextValue = new ArrayList<String>();

public AdapterClass(Context context, ArrayList<String> TextValue) {
    super(context, R.layout.row_of_drawer, TextValue);
    this.context = context;
    this.TextValue= TextValue;

}


@Override
public View getView(int position, View coverView, ViewGroup parent) {
    // TODO Auto-generated method stub

    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View rowView = inflater.inflate(R.layout.row_of_drawer,
            parent, false);

    TextView text1 = (TextView)rowView.findViewById(R.id.text1);
    text1.setText(TextValue.get(position));

    return rowView;

}

}

And create some change of your MainActivity.

use

   AdapterClass adClass = new AdapterClass(MainActivity.this, name_of_arrayList);

    mDrawerList.setAdapter(adClass);

insted of

 mDrawerList.setAdapter(new ArrayAdapter<String>(this,
        R.layout.drawer_list_item, mPlanetTitles));


回答3:

The string array is not the defining layout of the rows. Your R.layout.drawer_list_item represents the layout of each row of the list. If you want to include an image (or even other complicated row layout) you will include the image view into that custom layout xml replacing R.layout.drawer_list_item xml.

As for how to set the image or text into each different row, this is all done in the getView() method of the array adapter. A rough example is like this (assuming there is a textview and imageview in the row):

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    convertView = inflater.inflate(R.layout.revise_listview, parent,false);

    TextView tv = (TextView)convertView.findViewById(R.id.text);
    ImageView iv = (ImageView)convertView.findViewById(R.id.image);

    tv1.setText(stringArray.get(i));
    if(//condition) {
        iv.setImageResource(R.drawable.imagexx);
    }
    return convertView;
}

A more efficient way to implement the views for each row is using the viewholder pattern. One of many places you can find examples is here.



回答4:

To structure this really clearly I did a few extra lines of code for an object oriented implementation that generates everything from an array of NavigationItems - a plain Java class that contains fields for text, icon and fragment of a menu item:

In the MainActivity my Navigation is simply defined by the array:

NavigationAdapter navigationMenuAdapter;
NavigationItem[] navigationMenuItems = {
    new NavigationItem(R.string.menu_home, R.drawable.ic_menu_home, new MainFragment()),
    new NavigationItem(R.string.menu_statistics, R.drawable.ic_statistics, new StatisticsFragment()),
    new NavigationItem(R.string.menu_settings, R.drawable.ic_settings, new SettingsFragment()),
};

protected void onCreate(Bundle savedInstanceState) {
    ...
    navigationMenuAdapter = new NavigationAdapter(this, navigationMenuItems);
    mDrawerList.setAdapter(navigationMenuAdapter);
}

...
private void selectItem(int position) {
    // Insert the fragment by replacing any existing fragment
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.beginTransaction()
                   .replace(R.id.content_frame, navigationMenuItems[position].fragment)
                   .commit();

    mDrawerList.setItemChecked(position, true);
    setTitle(navigationMenuItems[position].stringTitle);
    mDrawerLayout.closeDrawer(mDrawerList);
}

The NavigationItem class:

public class NavigationItem
{
    /**
     * 
     * @param stringTitle The id of the string resource of the text of the item.
     * @param drawableIcon The id of the drawable resource of icon of the item.
     * @param fragment The Fragment to be loaded upon selecting the item.
     */
    public NavigationItem(int stringTitle, int drawableIcon, Fragment fragment)
    {
        this.stringTitle = stringTitle;
        this.drawableIcon = drawableIcon;
        this.fragment = fragment;
    }

    /**
     * The id of the string resource of the text of the item.
     */
    public int stringTitle;

    /**
     * The id of the drawable resource of icon of the item.
     */
    public int drawableIcon;

    /**
     * The Fragment to be loaded upon selecting the item.
     */
    public Fragment fragment;
}

And the NavigationAdapter:

public class NavigationAdapter extends BaseAdapter {
    private Context context;
    private NavigationItem[] navigationItems;

    public NavigationAdapter(Context context, NavigationItem[] items) {
        this.context = context;
        navigationItems = items;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row = null;
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row = inflater.inflate(R.layout.list_item_navigation, parent, false);
        } else {
            row = convertView;
        }

        TextView tvTitle = (TextView) row.findViewById(R.id.textView);
        ImageView ivIcon = (ImageView) row.findViewById(R.id.imageView);

        tvTitle.setText(navigationItems[position].stringTitle);
        ivIcon.setImageResource(navigationItems[position].drawableIcon);
        return row;
    }
}

which uses an very basic xml layout, list_item_navigation, that only contains an ImageView and a TextView.