Как создать прямоугольник с закругленными углами с разными сечениями разных цветов - PullRequest
0 голосов
/ 08 января 2019

ТРЕБОВАНИЕ

Как мне создать вид, похожий на этот.

enter image description here

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

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

ИДЕИ / ВЕЩИ, КОТОРЫЕ Я ПОПРОБОВАЛ

(1) Пользовательский вид, отображающий 3 прямоугольника рядом

Я пробовал собственный вид, который отображает 3 прямоугольника рядом. Но они, очевидно, имеют квадратные углы.

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    int viewHeight = 50;

    canvas.drawrect(0,  0, 60,  viewHeight, paint); // A
    canvas.drawrect(60, 0, 120, viewHeight, paint); // B
    canvas.drawrect(120,0, 180, viewHeight, paint); // C
}

enter image description here

(2) Форма с закругленными углами

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

<shape xmlns:android="http://schemas.android.com/apk/res/android">
     ...        
    <corners
        android:radius="4dp" />
    ....
</shape>

enter image description here

(3) Список слоев

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

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">       

    <item>
        <shape android:shape="rectangle">
            <size
                android:width="40dp"
                android:height="40dp" />
            <solid android:color="#F86F05" />
        </shape>
    </item>

    <item android:top="10dp">
        <shape android:shape="rectangle">
            <size
                android:width="30dp"
                android:height="30dp" />
            <solid android:color="#B31F19" />
        </shape>
    </item>

</layer-list>

enter image description here

(4) Список слоев с углами ??

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

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <corners
        android:radius="4dp" />

    <item>
        <shape android:shape="rectangle">
            <size
                android:width="40dp"
                android:height="40dp" />
            <solid android:color="#F86F05" />
        </shape>
    </item>

    <item android:top="10dp">
        <shape android:shape="rectangle">
            <size
                android:width="30dp"
                android:height="30dp" />
            <solid android:color="#B31F19" />
        </shape>
    </item>

</layer-list>

enter image description here

РЕЗЮМЕ

Этот последний приближается к моему требованию, однако

  • как программно указать ширину каждого "элемента"
  • как мне показать / скрыть "элемент" программно
  • как бы округлить только верхние наиболее видимые верхние углы "предмета" и нижние самые нижние углы "предмета"

ОБНОВЛЕНИЕ: КАК ДОБАВИТЬ ЛИФТ / СЕРУЮ ГРАНИЦУ

Спасибо "@ 0X0nosugar" за ваше решение. Теперь я хочу добавить высоту или небольшую серую границу, поскольку один из цветов очень светлый и близок к цвету фона. Когда я добавляю следующее, я получаю прямоугольную тень, которая выглядит ужасно с изогнутыми углами.

android:elevation="2dp"
android:outlineProvider="bounds"

enter image description here

Я бы хотел, чтобы это выглядело так, как показано ниже

enter image description here

Ответы [ 4 ]

0 голосов
/ 09 января 2019

Для дополнительного вопроса у вас есть два варианта. Используйте встроенные методы или нарисуйте свои (в зависимости от потребностей). Первый, вероятно, является правильным способом сделать это, если только вам не нужна пользовательская тень.

Встроенные методы

См. Раздел Outline в этом сообщении в блоге: https://android.jlelse.eu/mastering-shadows-in-android-e883ad2c9d5b

Пример кода из поста:

Создание OutlineProvider

public class ScalingLayoutOutlineProvider extends ViewOutlineProvider {

    @Override
    public void getOutline(View view, Outline outline) {
        outline.setRoundRect(0, 0, width, height, radius);
    }
}

Добавление провайдера структуры в ваше представление

public class ScalingLayout extends FrameLayout {

    //...
    viewOutline = new ScalingLayoutOutlineProvider(w, h, currentRadius);
    setOutlineProvider(viewOutline);
    //..

}

Рисуем свое

Для границы вы можете использовать View.getElevation (), чтобы получить желаемую высоту: https://developer.android.com/reference/android/view/View#getElevation(), а затем нарисовать другую фигуру позади полосы для тени.

0 голосов
/ 08 января 2019

Например (настроить цвета на желаемые, поля, высоты и т. Д.):

enter image description here

Вы можете создать это, используя LinearLayout в качестве корневого макета с ориентацией horizontal и назначив некоторые weights, создавая дочерние макеты и добавляя к ним собственные фоны. Простой, не слишком много вложенных макетов.

Например:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_margin="20dp"
    android:orientation="horizontal">

    <LinearLayout
        android:id="@+id/statsLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:clickable="true"
        android:background="@drawable/left_background">

        <TextView
            android:id="@+id/stats"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="A%"
            android:textAppearance="@style/TextAppearance.AppCompat.Title"
            android:textColor="@android:color/black" />
    </LinearLayout>


    <LinearLayout
        android:id="@+id/ncaaInfoLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:clickable="true"
        android:background="@drawable/middle_background">

        <TextView
            android:id="@+id/ncaaInfo"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="B%"
            android:textAppearance="@style/TextAppearance.AppCompat.Title"
            android:textColor="@android:color/black" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/accountLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:clickable="true"
        android:background="@drawable/right_background">

        <TextView
            android:id="@+id/account"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="C%"
            android:textAppearance="@style/TextAppearance.AppCompat.Title"
            android:textColor="@android:color/black" />
    </LinearLayout>
</LinearLayout>

Чем создать фон для каждого ребенка LinearLayout для левого left_bacground.xml:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
    android:bottomLeftRadius="30dp"
    android:topLeftRadius="30dp" />
<solid android:color="@android:color/holo_green_dark" />

Чем для LinearLayout in middle middle_background.xml`:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/holo_orange_dark" />

И последний LinearLayout на право right_bacgkround.xml:

  <?xml version="1.0" encoding="utf-8"?>
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
    android:bottomRightRadius="30dp"
    android:topRightRadius="30dp" />
<solid android:color="@android:color/holo_blue_dark" />

0 голосов
/ 08 января 2019

Вы можете создать пользовательский View, который будет рисовать прямоугольники на обрезанной части Canvas:

enter image description here

public class RoundedCornersSegmentedView extends View {

    private Paint paintA, paintB, paintC;
    private float cornerRadius;
    private float measuredWidth, measuredHeight;
    private RectF rect = new RectF(0, 0, 0,0);
    private Path rectPath = new Path();

    public RoundedCornersSegmentedView(Context context) {
        super(context);
        init();
    }

    public RoundedCornersSegmentedView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundedCornersSegmentedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setWillNotDraw(false);

        // add this so Canvas.clipPath() will give the desired result also for devices running Api level lower than 17,
        // see https://stackoverflow.com/a/30354461/5015207
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            setLayerType(LAYER_TYPE_SOFTWARE, null);
        }
        paintA = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintA.setColor(Color.GREEN);
        paintA.setStyle(Paint.Style.FILL);
        paintB = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintB.setColor(Color.YELLOW);
        paintB.setStyle(Paint.Style.FILL);
        paintC = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintC.setColor(Color.MAGENTA);
        paintC.setStyle(Paint.Style.FILL);

        // with  <dimen name="corner_radius">60dp</dimen> in res/values/dimens.xml
        cornerRadius = getResources().getDimensionPixelSize(R.dimen.corner_radius);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        measuredWidth = right - left;
        measuredHeight = bottom - top;
        rect.set(0, 0, measuredWidth, measuredHeight);
        rectPath.reset();
        rectPath.addRoundRect(rect, cornerRadius, cornerRadius, Path.Direction.CW);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.clipPath(rectPath);
        canvas.drawRect(0,0,measuredWidth/3f, measuredHeight, paintA);
        canvas.drawRect(measuredWidth/3f,0,2 * measuredWidth/3f, measuredHeight, paintB);
        canvas.drawRect(2 * measuredWidth/3f,0,measuredWidth, measuredHeight, paintC);
    }
}

Если вы хотите добавить какой-то полупрозрачный край, вы можете использовать Paint с прозрачным цветом и тип заливки Paint.Style.STROKE и нарисовать прямоугольник со скругленными углами.

Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// material color "Blue Gray 400",
// see https://material.io/design/color/the-color-system.html
shadowPaint.setColor(Color.argb(30, 120, 144, 156));
shadowPaint.setStyle(Paint.Style.STROKE);
shadowPaint.setStrokeWidth(30);

Прямоугольник (создание экземпляра вне onLayout() для лучшей производительности):

private RectF shadowRect = new RectF(0,0,0,0);

В onLayout():

int inset = 20;
shadowRect.set(inset, inset, measuredWidth - inset, measuredHeight - inset);

Вам следует переключать значение цвета / альфа-канала для тени Paint, а также значения ширины обводки и вставки, пока вы не подумаете, что это выглядит хорошо.

Применить в onDraw() после того, как вы нарисовали цветные сегменты:

canvas.drawRoundRect(shadowRect, cornerRadius, cornerRadius, shadowPaint);

enter image description here

Также может хорошо выглядеть (больше 3D), если вы сложите полупрозрачные Paint s с уменьшением ширины обводки и увеличением вставки, как создание собственного цветового градиента.

Спасибо @wblaschko за то, что поделились фрагментом кода на ViewOutlineProvider! Я добавил его в свой пример и получил следующий эффект:

enter image description here

Изменения в моем коде (примечание: возможно только для уровня 21 + API)

Внутренний класс кастомного вида:

@TargetApi(21)
static class ScalingOutlineProvider extends ViewOutlineProvider {
    private int cornerRadius;
    ScalingOutlineProvider(int cornerRadius){
        this.cornerRadius = cornerRadius;
    }
    @Override
    public void getOutline(View view, Outline outline) {
        outline.setRoundRect(0, 0, view.getWidth(), view.getHeight (), cornerRadius);
    }
}

И в конце init():

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
    // elevation of 4dp (cornerRadius was 60dp)
    setElevation(cornerRadius/15);
    setOutlineProvider(new ScalingOutlineProvider(cornerRadius));
}
0 голосов
/ 08 января 2019

Вы должны использовать CardView, посмотрите на этот пример:

<androidx.cardview.widget.CardView
  android:layout_width="match_parent"
  android:layout_height="180dp"
  android:layout_margin="@dimen/activity_vertical_margin"
  app:cardPreventCornerOverlap="false"
  app:cardCornerRadius="@dimen/activity_vertical_margin"
  android:foreground="?android:attr/selectableItemBackground">

  <androidx.constraintlayout.widget.ConstraintLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content">

      <View
        android:id="@+id/view1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#0c21a9"
        app:layout_constraintWidth_percent="0.33"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

      <View
        android:id="@+id/view2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#51c914"
        app:layout_constraintWidth_percent="0.33"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@id/view1" />

     <View
        android:id="@+id/view3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#a90c0c"
        app:layout_constraintWidth_percent="0.33"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@id/view2" />     

  </androidx.constraintlayout.widget.ConstraintLayout>     

</androidx.cardview.widget.CardView> 

Это свойство предотвращает перекрытие в закругленных углах app:cardPreventCornerOverlap="false"

Обратите внимание, что я использую lib androidx, рекомендованный Google AndroidX . Если вы предпочитаете, вы можете использовать устаревшую библиотеку поддержки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...