html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

Создаем ListView с Context Action Bar как в новом Gmail из песочницы



Что хотим получить


Сделать плавно работающий список с возможностью выделения рядов как кликом на иконку ряда, так и долгим нажатием на него. Также, дабы выделение не пропало даром, мы должны дать возможность пользователю производить некие действия с выделенными объектами.



Создание разметки для списка


Итак, в первую очередь нам потребуется создать layout, в котором будет находиться список, выглядит он так:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <ListView
            android:id="@android:id/list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"/>

</RelativeLayout>

Кроме множества падингов, любезно созданных для меня android developer studio, здесь ничего интересного нет. Разве что напомню: android:id/list — это специально выделенный ID, который знают ListActivity и ListFragment.

Далее создадим layout, который будет являться каждым рядом в нашем ListView:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="?android:attr/activatedBackgroundIndicator">

    <View
            android:id="@+id/item_image"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_margin="5dp"
            android:padding="10dp"/>

    <TextView
            android:id="@+id/item_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/item_image"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="10dp"
            android:text="TextView"
            android:layout_gravity="center_vertical|left"
            android:textAppearance="?android:textAppearanceListItem">
    </TextView>
</RelativeLayout>

Здесь у нас TextView, расположенный справа от View. На месте View обычно картинка, но в данном примере мы будем просто отображать случайно сгенерированный цвет.
Также обратите внимание на android:background="?android:attr/activatedBackgroundIndicator" в свойствах layout. Без этого атрибута не будет виден визуальный эффект выделения.

Создаем ListView и заполняем его


Сразу приведу код activity, а затем поясню его:
public class MainActivity extends ListActivity {
    public static final String TAG = "FOR_HABR";
    private Random randomGenerator = new Random();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //генерируем размер нашего листа
        int size = getRandomNumber(200);

        ListView listView = getListView();
        //Создаем инстанс нашего кастомного адаптера
        Integer[] colors = generateListOfColors(size).toArray(new Integer[0]);
        ArrayAdapter<Integer> customAdapter = new CustomAdapter(this, R.layout.list_view_row, colors, listView);
        listView.setAdapter(customAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Log.d(TAG, "onCreateOptionsMenu");
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    //Генерируем список из случайных цветов
    private List<Integer> generateListOfColors(int size) {
        List<Integer> result = new ArrayList<Integer>();
        for (int i = 0; i < size; i++) {
            result.add(generateRandomColor());
        }
        return result;
    }

    //Генерируем случайный цвет
    private int generateRandomColor() {
        return Color.rgb(getRandomNumber(256), getRandomNumber(256), getRandomNumber(256));
    }

    private int getRandomNumber(int maxValue) {
        return randomGenerator.nextInt(maxValue);
    }

}

Здесь мы первым делом находим по ID layout, в котором будет размещен наш лист, и назначаем его контентом этого activity. Для того чтобы заполнить ListView информацией, мы в начале генерируем список из чисел и передаем его в конструктор нашего кастомного адаптера.
Адаптер — это мост между данными и отображением, в нем мы подсказываем системе, где и какой компонент каждого ряда списка мы хотели бы видеть. Вот код нашего адаптера:
public class CustomAdapter extends ArrayAdapter<Integer> {
    private ListView listView;

    public CustomAdapter(Context context, int textViewResourceId, Integer[] objects, ListView listView) {
        super(context, textViewResourceId, objects);
        this.listView = listView;
    }

    static class ViewHolder {
        TextView text;
        View indicator;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        Integer color = getItem(position);

        View rowView = convertView;

        //Небольшая оптимизация, которая позволяет повторно использовать объекты
        if (rowView == null) {
            LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater();
            rowView = inflater.inflate(R.layout.list_view_row, parent, false);
            ViewHolder h = new ViewHolder();
            h.text = (TextView) rowView.findViewById(R.id.item_text);
            h.indicator = rowView.findViewById(R.id.item_image);
            rowView.setTag(h);
        }

        ViewHolder h = (ViewHolder) rowView.getTag();

        h.text.setText("#" + Integer.toHexString(color).replaceFirst("ff", ""));
        h.indicator.setBackgroundColor(color);

        return rowView;
    }
}

Мы переписываем всего один метод из родительского класса — метод getView. Этот метод вызывается каждый раз, когда в поле зрения пользователя появляется новый ряд списка. Соответственно, из него мы должны вернуть объект View именно в том виде, в котором желаем отобразить его пользователю.
Здесь мы применяем популярный шаблон, который позволяет нам немного (до 15%) увеличить производительность ListView за счет повторного использования объектов. Более подробно прочитать про этот шаблон можно здесь.

На этом этапе можно запустить приложении, и мы увидим список с цветами, но, конечно, без какого-либо интерактива.

Добавляем возможность выбора ряда


Для этого требуется сделать следующие:
//Указываем ListView, что мы хотим режим с мультивыделением
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
//Указываем обработчик такого режима
listView.setMultiChoiceModeListener(new MultiChoiceImpl(listView));


Обработчик выглядит так:
public class MultiChoiceImpl implements AbsListView.MultiChoiceModeListener {
    private AbsListView listView;

    public MultiChoiceImpl(AbsListView listView) {
        this.listView = listView;
    }

    @Override
    //Метод вызывается при любом изменении состояния выделения рядов
    public void onItemCheckedStateChanged(ActionMode actionMode, int i, long l, boolean b) {
        Log.d(MainActivity.TAG, "onItemCheckedStateChanged");
        int selectedCount = listView.getCheckedItemCount();
        //Добавим количество выделенных рядов в Context Action Bar
        setSubtitle(actionMode, selectedCount);
    }

    @Override
    //Здесь надуваем CAB из xml
    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
        Log.d(MainActivity.TAG, "onCreateActionMode");
        MenuInflater inflater = actionMode.getMenuInflater();
        inflater.inflate(R.menu.context_menu, menu);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
        Log.d(MainActivity.TAG, "onPrepareActionMode");
        return false;
    }

    @Override
   //Вызывается при клике на любой Item из СAB
    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
        String text = "Action - " + menuItem.getTitle() + " ; Selected items: " + getSelectedFiles();
        Toast.makeText(listView.getContext(), text , Toast.LENGTH_LONG).show();
        return false;
    }

    @Override
    public void onDestroyActionMode(ActionMode actionMode) {
        Log.d(MainActivity.TAG, "onDestroyActionMode");
    }

    private void setSubtitle(ActionMode mode, int selectedCount) {
        switch (selectedCount) {
            case 0:
                mode.setSubtitle(null);
                break;
            default:
                mode.setTitle(String.valueOf(selectedCount));
                break;
        }
    }

    private List<String> getSelectedFiles() {
        List<String> selectedFiles = new ArrayList<String>();

        SparseBooleanArray sparseBooleanArray = listView.getCheckedItemPositions();
        for (int i = 0; i < sparseBooleanArray.size(); i++) {
            if (sparseBooleanArray.valueAt(i)) {
                Integer selectedItem = (Integer) listView.getItemAtPosition(sparseBooleanArray.keyAt(i));
                selectedFiles.add("#" + Integer.toHexString(selectedItem).replaceFirst("ff", ""));
            }
        }
        return selectedFiles;
    }
}

Вероятно, вы заметили, что здесь мы надуваем новый Action Bar (context_menu). Он выглядит так:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:id="@+id/cab_add"
            android:icon="@android:drawable/ic_menu_add"
            android:orderInCategory="1"
            android:showAsAction="ifRoom"
            android:title="add"/>
    <item
            android:id="@+id/cab_share"
            android:icon="@android:drawable/ic_menu_share"
            android:orderInCategory="1"
            android:showAsAction="ifRoom"
            android:title="share"/>
</menu>

Итак, теперь по порядку. В ListView мы устанавливаем специальный режим выделения — CHOICE_MODE_MULTIPLE_MODAL, который подразумевает, что мы подсунем ListView класс, реализующий интерфейс AbsListView.MultiChoiceModeListener. В этом классе мы реализуем методы, в которых указываем, что хотим получить на событие выделения, клика по item в CAB или на уничтожение CAB.

Теперь осталось добавить возможность выделения ряда по клику на иконку. Для этого требуется навесить на нее в методе getView OnClickListener:
h.indicator.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        selectRow(v);
    }

    private void selectRow(View v) {
        listView.setItemChecked(position, !isItemChecked(position));
    }

    private boolean isItemChecked(int pos) {
        SparseBooleanArray sparseBooleanArray = listView.getCheckedItemPositions();
        return sparseBooleanArray.get(pos);
    }
});

Здесь, в случае если ряд уже выделен, снимаем выделение, в противном случае выделяем.

На этом все. Полный код примера можно найти у меня на BitBucket.

UPD. Практически все, что использовано в этой статье было добавлено в API 11, а кое-что даже в 14. Так, что если хотите совместимости с API < 11, советую посмотреть сюда.
Так же есть замечательная разработка товарища HoloEverywhere
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

0

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • habrahabr.ru
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции