Memperbesar bagian tengah drawable gradien Android

Saya membuat gambar gradien Android yang bagian atas dan bawahnya berada hitam dan bagian tengahnya transparan:

<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>

Gradien yang dirender terlihat seperti ini:

gambar gradien

Seperti yang Anda lihat, bagian hitam menyebar ke sebagian besar layar. Saya ingin warna hitam hanya terlihat pada sebagian kecil bagian atas dan bawah. Adakah cara untuk memperbesar bagian tengah transparan, atau memperkecil garis hitam atas dan bawah?

Saya mencoba bermain-main dengan beberapa atribut XML lain yang disebutkan dalam dokumentasi GradientDrawable tertaut, namun tidak ada satupun yang membuat dan membedakan.


person Tot Zam    schedule 16.03.2017    source sumber
comment
jika tidak ada cara untuk menyetel lebih dari satu warna dengan atribut xml, Anda dapat mencoba mendistorsi sumber daya dapat digambar, jika memungkinkan. jika tidak, gunakan secara terprogram   -  person lelloman    schedule 16.03.2017
comment
@lelloman Saya juga terbuka untuk saran terprogram, meskipun tetap menggunakan xml saja mungkin akan lebih mudah. Apakah Anda punya ide?   -  person Tot Zam    schedule 16.03.2017
comment
Saya pikir Anda memerlukan LinearGradient dengan hitam - putih - hitam tetapi tidak pada jarak yang sama, saya khawatir GradientDrawable tidak memungkinkan Anda mengubah posisi warna, apakah Anda akan menggunakan Drawable khusus secara terprogram?   -  person lelloman    schedule 16.03.2017
comment
@lelloman Terima kasih telah merespons. Saya perlu mencoba solusi Anda. Sekadar menunjukkan agar tidak bingung, warna tengahnya transparan, bukan putih.   -  person Tot Zam    schedule 16.03.2017
comment
seharusnya tidak menjadi masalah, menurut saya Anda bisa menggantinya dengan 0, Anda juga dapat mencoba dengan jumlah langkah dan warna yang berbeda   -  person lelloman    schedule 16.03.2017
comment
Anda dapat membuat bagian hitam menjadi persentase tinggi tampilan dengan melapiskan dua gradien drawable. Periksa inti gist.github.com/luksprog/4ac6e7550564b3823840d4a0943c8dd6 untuk contohnya (pada contoh di atas bagian hitam masing-masing akan menempati 10% dari tinggi tampilan).   -  person user    schedule 16.03.2017
comment
@Luksprog Setelah melihat saran pertama thenaoh, saya sebenarnya berpikir untuk mencoba sesuatu seperti itu. Dalam hal ini, apakah centerColor benar-benar melakukan sesuatu, atau dapatkah saya mengabaikannya?   -  person Tot Zam    schedule 16.03.2017
comment
Idenya adalah menggunakan centerColor dan centerY sebagai startColor semu. Kalau tidak, tanpa centerColor, saya pikir gradien hanya akan menyebar ke seluruh ketinggian (antara transparan dan hitam).   -  person user    schedule 16.03.2017


Jawaban (3)


Untuk solusi XML saja, Anda dapat membuat layer-list dengan dua objek gradient terpisah.

Kode berikut membuat dua objek gradient yang tumpang tindih dan menggunakan centerY dengan centerColor untuk mengimbangi bagian hitam. Di sini, atribut centerY disetel ke 0.9 dan 0.1, sehingga warna hitam dibatasi pada 10% atas dan bawah dari tinggi tampilan.

<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>

Untuk API level 23 atau lebih tinggi, solusi berikut juga akan berfungsi, menggunakan android:height. Solusi ini dapat bekerja bahkan jika Anda tidak mengetahui tinggi total tampilan Anda, selama Anda mengetahui seberapa besar gradien yang Anda inginkan.

Kode ini membuat dua gradien terpisah, masing-masing dengan tinggi 60sp, lalu menggunakan android:gravity untuk mengapungkan gradien ke atas dan bawah tampilan.

<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>

Terima kasih @Luksprog untuk bantuan kode, dan @thenaoh untuk awal ide.

Solusi di atas berfungsi dan bagus karena merupakan XML murni. Jika gradien Anda ditampilkan dengan garis-garis, Anda mungkin ingin mencoba solusi terprogram, seperti yang ditunjukkan dalam jawaban @lelloman, untuk membuat gradien yang lebih halus.

person Tot Zam    schedule 17.03.2017

Inilah cara melakukannya dengan Drawable khusus. Anda dapat menyetel LinearGradient sesuai keinginan, lalu mengaturnya sebagai latar belakang tampilan dengan 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

Ada solusinya, dengan asumsi Anda mengetahui terlebih dahulu ketinggian tampilan Anda (misalkan di sini 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>

Namun jika Anda tidak mengetahui ketinggiannya terlebih dahulu, solusi lain adalah dengan membuat tampilan kustom sendiri, seperti ini:

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);
    }
}

Semoga ini bisa membantu.

person matteoh    schedule 16.03.2017
comment
Ketinggian pandangan saya bukanlah ukuran yang konstan, jadi solusi pertama tidak akan berhasil. Apakah ada cara untuk menggunakan solusi itu dengan persentase? - person Tot Zam; 16.03.2017
comment
Saya rasa tidak, seperti yang Anda lihat di sini: stackoverflow.com/a/6061494/3527024 . Jadi itu sebabnya saya menyarankan opsi kedua. - person matteoh; 16.03.2017
comment
opsi kedua terlihat bagus, namun alih-alih membuat subkelas ImageView Anda dapat melakukan hal yang sama dengan membuat subkelas Drawable dan menyetelnya sebagai latar belakang ke tampilan apa pun yang Anda inginkan - person lelloman; 16.03.2017
comment
Saya baru saja mencoba solusi pertama Anda, dan solusi tersebut tidak menghasilkan gradien yang benar. Warna hitam menyebar ke seluruh area. - person Tot Zam; 17.03.2017