Подтвердить что ты не робот

ListView с элементами треугольной формы

введите описание изображения здесь

Мне нужно реализовать ListView с элементами треугольной формы, как показано на этом изображении. Представления, которые добавляются в ListView, обычно имеют прямоугольную форму. Даже в документации вид рассматривается как "занимает прямоугольную область на экране и отвечает за рисование и обработку событий".

Как я могу добавить в ListView непрямоугольные фигуры и в то же время убедиться, что область щелчка ограничена формой, в данном случае треугольником.

Спасибо!

4b9b3361

Ответ 1

Мое решение будет использовать перекрывающиеся представления, которые обрезаются на чередующиеся треугольники и принимают только события касания в пределах своего треугольника.

Проблема заключается в том, что ListView на самом деле не поддерживает перекрывающиеся элементы Views, поэтому мой пример просто загружает все элементы сразу в ScrollView, что может быть плохо, если у вас больше, чем, скажем, 30 элементов. Возможно, это выполнимо с помощью RecyclerView, но я не рассматривал это.

Я решил расширить FrameLayout для реализации треугольника View logic, поэтому вы можете использовать его как корневой элемент View для элемента списка и поместить в него все, что вам нужно:

public class TriangleFrameLayout extends FrameLayout {

    // TODO: constructors

    public enum Align { LEFT, RIGHT };

    private Align alignment = Align.LEFT;

    /**
     * Specify whether it a left or a right triangle.
     */
    public void setTriangleAlignment(Align alignment) {
        this.alignment = alignment;
    }

    @Override
    public void draw(Canvas canvas) {
        // crop drawing to the triangle shape
        Path mask = new Path();
        Point[] tria = getTriangle();
        mask.moveTo(tria[0].x, tria[0].y);
        mask.lineTo(tria[1].x, tria[1].y);
        mask.lineTo(tria[2].x, tria[2].y);
        mask.close();

        canvas.save();

        canvas.clipPath(mask);
        super.draw(canvas);

        canvas.restore();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // check if touch event is within the triangle shape
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
            Point touch = new Point((int) event.getX(), (int) event.getY());
            Point[] tria = getTriangle();

            if (!isPointInsideTrigon(touch, tria[0], tria[1], tria[2])) {
                // ignore touch event outside triangle
                return false;
            }
        }

        return super.onTouchEvent(event);
    }

    private boolean isPointInsideTrigon(Point s, Point a, Point b, Point c) {
        // stolen from http://stackoverflow.com/a/9755252
        int as_x = s.x - a.x;
        int as_y = s.y - a.y;
        boolean s_ab = (b.x - a.x) * as_y - (b.y - a.y) * as_x > 0;
        if ((c.x - a.x) * as_y - (c.y - a.y) * as_x > 0 == s_ab)
            return false;
        if ((c.x - b.x) * (s.y - b.y) - (c.y - b.y) * (s.x - b.x) > 0 != s_ab)
            return false;
        return true;
    }

    private Point[] getTriangle() {
        // define the triangle shape of this View
        boolean left = alignment == Align.LEFT;
        Point a = new Point(left ? 0 : getWidth(), -1);
        Point b = new Point(left ? 0 : getWidth(), getHeight() + 1);
        Point c = new Point(left ? getWidth() : 0, getHeight() / 2);
        return new Point[] { a, b, c };
    }

}

Пример XML-макета элемента с TriangleFrameLayout как root может выглядеть следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<your.package.TriangleFrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_triangle"
    android:layout_width="match_parent"
    android:layout_height="160dp"
    android:layout_marginTop="-80dp"
    android:clickable="true"
    android:foreground="?attr/selectableItemBackground">

    <TextView
        android:id="@+id/item_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="20dp"
        android:textSize="30dp"
        android:textStyle="bold"
        android:textColor="#ffffff" />

</your.package.TriangleFrameLayout>

Здесь у нас фиксированная высота 160dp, которую вы можете изменить на все, что захотите. Важным является отрицательный верхний край половины высоты, -80dp в этом случае, который заставляет элементы перекрываться и разные треугольники совпадают.

Теперь мы можем раздуть несколько таких элементов и добавить их в список, т.е. ScrollView. Это показывает пример макета для нашей Activity или Framgent:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

    </LinearLayout>

</ScrollView>

И код для заполнения списка:

Здесь я создал фиктивный адаптер, аналог ListView, который просто перечисляет наши элементы от 0 до 15.

    ListAdapter adapter = new BaseAdapter() {
        @Override
        public int getCount() { return 16; }

        @Override
        public Integer getItem(int position) { return position; }

        @Override
        public long getItemId(int position) { return position; }

        @Override
        public View getView(int position, View view, ViewGroup parent) {
            if (view == null) {
                view = getLayoutInflater().inflate(R.layout.item_tria, parent, false);
            }

            // determine whether it a left or a right triangle
            TriangleFrameLayout.Align align =
                    (position & 1) == 0 ? TriangleFrameLayout.Align.LEFT : TriangleFrameLayout.Align.RIGHT;

            // setup the triangle
            TriangleFrameLayout triangleFrameLayout = (TriangleFrameLayout) view.findViewById(R.id.root_triangle);
            triangleFrameLayout.setTriangleAlignment(align);
            triangleFrameLayout.setBackgroundColor(Color.argb(255, 0, (int) (Math.random() * 256), (int) (Math.random() * 256)));

            // setup the example TextView
            TextView textView = (TextView) view.findViewById(R.id.item_text);
            textView.setText(getItem(position).toString());
            textView.setGravity((position & 1) == 0 ? Gravity.LEFT : Gravity.RIGHT);

            return view;
        }
    };

    // populate the list
    LinearLayout list = (LinearLayout) findViewById(R.id.list);
    for (int i = 0; i < adapter.getCount(); ++i) {
        final int position = i;
        // generate the item View
        View item = adapter.getView(position, null, list);
        list.addView(item);
        item.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Toast.makeText(v.getContext(), "#" + position, Toast.LENGTH_SHORT).show();
            }
        });
    }

В конце мы имеем результат, который выглядит так:

введите описание изображения здесь

Ответ 2

Два элемента списка для одной строки и сделайте текст кликабельным.

  • Создание изображений для каждой строки и двух изображений для одного элемента.
  • Для каждой опции нужно сделать только текст для обоих элементов, которые можно щелкнуть.

введите описание изображения здесь

Ответ 3

Я не думаю, что можно создать реальные треугольные представления и добавить их в список. И какой формат вы бы использовали в таком сценарии?

Один из способов сделать это - использовать фоновые изображения для создания иллюзии. Подумайте о каждом сегменте, отделенном красными линиями как элемент в виде списка. Поэтому вам нужно создать фоновое изображение, необходимое для каждого элемента в представлении списка, и установить их в правильном порядке.

введите описание изображения здесь

Обновление: Это то, что я подразумеваю под последовательной нарезкой фонового изображения в моих комментариях ниже.

введите описание изображения здесь

Ответ 4

Для тех, кто сейчас использует RecyclerView, реализация ItemDecoration можно установить в RecyclerView, который будет:

  • Смещение элементов: переопределение getItemOffsets() оформления. Здесь можно перемещать объекты так, чтобы они перекрывались.
  • Рисовать фигуры: переопределить onDraw(), чтобы нарисовать треугольники в качестве фона.