ขยายศูนย์กลางของการไล่ระดับสีของ Android ที่วาดได้

ฉันสร้าง การไล่ระดับสีของ Android ที่วาดได้ โดยที่ด้านบนและด้านล่างอยู่ สีดำและตรงกลางโปร่งใส:

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

    <gradient
        android:startColor="@android:color/black"
        android:centerColor="@android:color/transparent"
        android:endColor="@android:color/black"
        android:angle="90"/>
</shape>

การไล่ระดับสีที่แสดงผลมีลักษณะดังนี้:

ภาพไล่ระดับสี

อย่างที่คุณเห็น ชิ้นส่วนสีดำกระจายไปเกือบทั้งหน้าจอ ฉันต้องการให้สีดำแสดงเพียงส่วนเล็กๆ บนและล่างเท่านั้น มีวิธีใดที่ฉันจะทำให้จุดกึ่งกลางโปร่งใสใหญ่ขึ้น หรือทำให้แถบสีดำด้านบนและด้านล่างเล็กลงได้หรือไม่

ฉันลองใช้คุณลักษณะ XML อื่นๆ บางส่วนที่กล่าวถึงในเอกสาร GradientDrawable ที่เชื่อมโยงไว้ แต่ดูเหมือนจะไม่มีคุณลักษณะใดที่สร้างความแตกต่างได้


person Tot Zam    schedule 16.03.2017    source แหล่งที่มา
comment
หากไม่มีวิธีตั้งค่ามากกว่าหนึ่งสีด้วยแอตทริบิวต์ xml คุณสามารถลองบิดเบือนสิ่งที่วาดได้หากเป็นไปได้ มิฉะนั้นไปโดยทางโปรแกรม   -  person lelloman    schedule 16.03.2017
comment
@lelloman ฉันเปิดรับข้อเสนอแนะแบบเป็นโปรแกรมเช่นกัน แม้ว่าการใช้แค่ xml อาจจะง่ายกว่าก็ตาม คุณมีความคิดบ้างไหม?   -  person Tot Zam    schedule 16.03.2017
comment
ฉันคิดว่าคุณต้องการ LinearGradient ที่มีสีดำ - สีขาว - สีดำ แต่เพียงไม่อยู่ในระยะห่างเดียวกัน ฉันเกรงว่า GradientDrawable ไม่อนุญาตให้คุณปรับแต่งตำแหน่งของสี คุณจะใช้ Drawable แบบกำหนดเองโดยทางโปรแกรมหรือไม่   -  person lelloman    schedule 16.03.2017
comment
@lelloman ขอบคุณสำหรับการตอบกลับ ฉันจะต้องลองวิธีแก้ปัญหาของคุณ เพียงชี้ให้เห็นเพื่อป้องกันความสับสนสีกลางคือโปร่งใสไม่ใช่สีขาว   -  person Tot Zam    schedule 16.03.2017
comment
มันไม่น่าจะเป็นปัญหา ฉันคิดว่าคุณสามารถแทนที่มันด้วย 0 ได้ และคุณยังสามารถลองใช้จำนวนขั้นตอนและสีที่แตกต่างกันได้   -  person lelloman    schedule 16.03.2017
comment
คุณสามารถทำให้ส่วนสีดำเป็นเปอร์เซ็นต์ของความสูงของมุมมองได้โดยการวางเลเยอร์ที่วาดแบบไล่ระดับได้ 2 ชั้น ตรวจสอบส่วนสำคัญนี้ gist.github.com/luksprog/4ac6e7550564b3823840d4a0943c8dd6 เพื่อดูตัวอย่าง (ในตัวอย่างด้านบน ส่วนสีดำแต่ละส่วนจะใช้พื้นที่ 10% ของความสูงของมุมมอง)   -  person user    schedule 16.03.2017
comment
@Luksprog หลังจากเห็นคำแนะนำแรกของ thenaoh ฉันก็คิดจะลองทำแบบนั้นจริงๆ ในกรณีนี้ centerColor ทำอะไรได้จริงหรือฉันสามารถละเว้นได้หรือไม่   -  person Tot Zam    schedule 16.03.2017
comment
แนวคิดคือการใช้ centerColor และ centerY เป็น startColor หลอก มิฉะนั้นหากไม่มีสีตรงกลาง ฉันคิดว่าการไล่ระดับสีจะกระจายความสูงทั้งหมด (ระหว่างโปร่งใสและสีดำ)   -  person user    schedule 16.03.2017


คำตอบ (3)


สำหรับโซลูชัน XML เท่านั้น คุณสามารถสร้าง layer-list ด้วยอ็อบเจ็กต์ gradient แยกกันสองอ็อบเจ็กต์

รหัสต่อไปนี้สร้างวัตถุ gradient สองรายการที่ทับซ้อนกัน และใช้ centerY กับ centerColor เพื่อชดเชยส่วนสีดำ ในที่นี้ แอตทริบิวต์ centerY ถูกตั้งค่าเป็น 0.9 และ 0.1 ดังนั้นสีดำจึงถูกจำกัดอยู่ที่ด้านบนและด้านล่าง 10% ของความสูงของมุมมอง

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <gradient
                android:angle="90"
                android:centerColor="@android:color/transparent"
                android:centerY="0.9"
                android:endColor="@android:color/black"
                android:startColor="@android:color/transparent" />
        </shape>
    </item>

    <item>
        <shape>
            <gradient
                android:angle="90"
                android:centerColor="@android:color/transparent"
                android:centerY="0.1"
                android:endColor="@android:color/transparent"
                android:startColor="@android:color/black" />
        </shape>
    </item>
</layer-list>

สำหรับ API ระดับ 23 หรือสูงกว่า โซลูชันต่อไปนี้จะใช้งานได้โดยใช้ android:height วิธีนี้สามารถใช้ได้แม้ว่าคุณจะไม่ทราบความสูงรวมของมุมมอง ตราบใดที่คุณรู้ว่าคุณต้องการไล่ระดับสีใหญ่แค่ไหน

โค้ดนี้สร้างการไล่ระดับสีสองแบบแยกกัน โดยแต่ละแบบมีความสูง 60sp จากนั้นใช้ android:gravity เพื่อลอยการไล่ระดับสีไปที่ด้านบนและด้านล่างของมุมมอง

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:height="60sp"
        android:gravity="top">
        <shape android:shape="rectangle">
            <gradient
                android:angle="90"
                android:endColor="@android:color/black"
                android:startColor="@android:color/transparent" />
        </shape>
    </item>
    <item
        android:height="65sp"
        android:gravity="bottom">
        <shape android:shape="rectangle">
            <gradient
                android:angle="90"
                android:endColor="@android:color/transparent"
                android:startColor="@android:color/black" />
        </shape>
    </item>
</layer-list>

ขอบคุณ @Luksprog สำหรับความช่วยเหลือด้านโค้ด และ @thenaoh สำหรับการเริ่มต้นแนวคิด

โซลูชันข้างต้นใช้งานได้และเป็นเรื่องดีที่เป็น XML ล้วนๆ หากการไล่ระดับสีของคุณแสดงเป็นแถบ คุณอาจต้องการลองใช้โซลูชันแบบเป็นโปรแกรม เช่น ที่แสดงในคำตอบของ @lelloman's เพื่อสร้าง การไล่ระดับสีที่นุ่มนวลยิ่งขึ้น

person Tot Zam    schedule 17.03.2017

ต่อไปนี้คือวิธีการดำเนินการด้วย Drawable ที่กำหนดเอง คุณสามารถปรับแต่ง LinearGradient ตามที่คุณต้องการ จากนั้นตั้งเป็นพื้นหลังของมุมมองด้วย view.setBackground(new CustomDrawable());

public class CustomDrawable extends Drawable {

    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int[] colors;
    private float[] positions;

    public CustomDrawable() {
        paint.setStyle(Paint.Style.FILL);
        this.colors = new int[]{0xff000000, 0xffaaaaaa, 0xffffffff, 0xffaaaaaa, 0xff000000};
        this.positions = new float[]{.0f, .2f, .5f, .8f, 1.f};
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);    

        LinearGradient linearGradient = new LinearGradient(left, top,left, bottom, colors, positions, Shader.TileMode.CLAMP);
        paint.setShader(linearGradient);
    }

    @Override
    public void draw(@NonNull Canvas canvas) {    
        canvas.drawRect(getBounds(), paint);
    }

    @Override
    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
        paint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        paint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSPARENT;
    }
}
person lelloman    schedule 16.03.2017

มีวิธีแก้ไข โดยสมมติว่าคุณรู้ล่วงหน้าถึงความสูงของมุมมองของคุณ (สมมติว่าที่นี่ 60dp):

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:bottom="40dp">
    <shape android:shape="rectangle">
        <gradient
            android:type="linear"
            android:angle="90"
            android:startColor="#FFFFFF"
            android:endColor="#000000"/>
    </shape>
</item>
<item
    android:top="20dp"
    android:bottom="20dp"
    android:gravity="center_vertical">
    <shape android:shape="rectangle">
        <solid android:color="#FFFFFF"/>
    </shape>
</item>
<item
    android:top="40dp"
    android:gravity="bottom">
    <shape android:shape="rectangle">
        <gradient
            android:type="linear"
            android:angle="90"
            android:startColor="#000000"
            android:endColor="#FFFFFF"/>
    </shape>
</item>
</layer-list>

แต่ถ้าคุณไม่ทราบความสูงล่วงหน้า อีกวิธีหนึ่งคือสร้างมุมมองที่คุณกำหนดเอง เช่นนี้

public class MyView extends ImageView
{
    private Paint paint = null;

    public MyView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        paint.setShader(getLinearGradient(0, getHeight()));
        canvas.drawPaint(paint);
    }

    private LinearGradient getLinearGradient(float y0, float y1)
    {
        // colors :
        int[] listeColors = new int[3];
        listeColors[0] = 0xFF000000;
        listeColors[1] = 0xFFFFFFFF;
        listeColors[2] = 0xFFFFFFFF;

        // positions :
        float[] listPositions = new float[3];
        listPositions[0] = 0;
        listPositions[1] = 0.25F;
        listPositions[2] = 1;

        // gradient :
        return new LinearGradient(0, y0, 0, y0 + (y1 - y0) / 2, listeColors, listPositions, Shader.TileMode.MIRROR);
    }
}

หวังว่ามันจะช่วยได้

person matteoh    schedule 16.03.2017
comment
ความสูงของการมองเห็นของฉันไม่ใช่ขนาดคงที่ ดังนั้นวิธีแก้ปัญหาแรกจึงไม่ได้ผล มีวิธีใช้วิธีแก้ปัญหานั้นเป็นเปอร์เซ็นต์หรือไม่? - person Tot Zam; 16.03.2017
comment
ฉันไม่คิดอย่างนั้น อย่างที่คุณเห็นที่นี่: stackoverflow.com/a/6061494/3527024 นั่นเป็นเหตุผลที่ฉันแนะนำตัวเลือกที่สองของฉัน - person matteoh; 16.03.2017
comment
ตัวเลือกที่สองดูดี แต่แทนที่จะจัดคลาสย่อย ImageView คุณสามารถทำสิ่งเดียวกันโดยจัดคลาสย่อย Drawable และตั้งค่าเป็นพื้นหลังสำหรับมุมมองใดก็ได้ที่คุณต้องการ - person lelloman; 16.03.2017
comment
ฉันเพิ่งลองใช้วิธีแก้ไขปัญหาแรกของคุณ แต่ไม่ได้เรนเดอร์การไล่ระดับสีที่ถูกต้อง สีดำกระจายไปทั่วบริเวณ - person Tot Zam; 17.03.2017