Hình dạng nhiều độ dốc


177

Tôi muốn tạo một hình dạng giống như hình ảnh sau đây:

văn bản thay thế

Lưu ý nửa độ dốc trên cùng từ màu 1 đến màu 2, nhưng có một nửa dưới đó chuyển màu từ màu 3 sang màu 4. Tôi biết cách tạo hình với một độ dốc duy nhất, nhưng tôi không chắc cách chia hình thành hai nửa và tạo 1 hình với 2 độ dốc khác nhau.

Có ý kiến ​​gì không?


Câu trả lời:


383

Tôi không nghĩ bạn có thể làm điều này bằng XML (ít nhất là không phải trong Android), nhưng tôi đã tìm thấy một giải pháp tốt được đăng ở đây có vẻ như nó sẽ là một trợ giúp tuyệt vời!

ShapeDrawable.ShaderFactory sf = new ShapeDrawable.ShaderFactory() {
    @Override
    public Shader resize(int width, int height) {
        LinearGradient lg = new LinearGradient(0, 0, width, height,
            new int[]{Color.GREEN, Color.GREEN, Color.WHITE, Color.WHITE},
            new float[]{0,0.5f,.55f,1}, Shader.TileMode.REPEAT);
        return lg;
    }
};

PaintDrawable p=new PaintDrawable();
p.setShape(new RectShape());
p.setShaderFactory(sf);

Về cơ bản, mảng int cho phép bạn chọn nhiều điểm dừng màu và mảng float sau xác định vị trí các điểm dừng đó (từ 0 đến 1). Sau đó, bạn có thể, như đã nêu, chỉ cần sử dụng điều này như một Drawable tiêu chuẩn.

Chỉnh sửa: Đây là cách bạn có thể sử dụng điều này trong kịch bản của bạn. Giả sử bạn có một Nút được định nghĩa bằng XML như vậy:

<Button
    android:id="@+id/thebutton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Press Me!"
    />

Sau đó, bạn sẽ đặt một cái gì đó như thế này vào phương thức onCreate () của bạn:

Button theButton = (Button)findViewById(R.id.thebutton);
ShapeDrawable.ShaderFactory sf = new ShapeDrawable.ShaderFactory() {
    @Override
    public Shader resize(int width, int height) {
        LinearGradient lg = new LinearGradient(0, 0, 0, theButton.getHeight(),
            new int[] { 
                Color.LIGHT_GREEN, 
                Color.WHITE, 
                Color.MID_GREEN, 
                Color.DARK_GREEN }, //substitute the correct colors for these
            new float[] {
                0, 0.45f, 0.55f, 1 },
            Shader.TileMode.REPEAT);
         return lg;
    }
};
PaintDrawable p = new PaintDrawable();
p.setShape(new RectShape());
p.setShaderFactory(sf);
theButton.setBackground((Drawable)p);

Tôi không thể kiểm tra điều này vào lúc này, đây là mã từ đầu của tôi, nhưng về cơ bản chỉ cần thay thế hoặc thêm điểm dừng cho các màu mà bạn cần. Về cơ bản, trong ví dụ của tôi, bạn sẽ bắt đầu với màu lục nhạt, nhạt dần sang màu trắng trước trung tâm (để làm mờ dần, thay vì chuyển đổi khắc nghiệt), mờ dần từ trắng sang giữa xanh giữa 45% và 55%, sau đó mờ dần từ giữa xanh đến xanh đậm từ 55% đến cuối. Điều này có thể trông không giống hệt hình dạng của bạn (Ngay bây giờ, tôi không có cách nào để kiểm tra các màu này), nhưng bạn có thể sửa đổi điều này để sao chép ví dụ của mình.

Chỉnh sửa: Ngoài ra, tham chiếu 0, 0, 0, theButton.getHeight()đến tọa độ x0, y0, x1, y1 của gradient. Vì vậy, về cơ bản, nó bắt đầu ở x = 0 (bên trái), y = 0 (trên cùng) và kéo dài đến x = 0 (chúng tôi muốn có độ dốc dọc, do đó không cần góc trái sang phải), y = chiều cao của nút. Vì vậy, độ dốc đi ở một góc 90 độ từ đỉnh nút đến dưới cùng của nút.

Chỉnh sửa: Được rồi, vì vậy tôi có thêm một ý tưởng hoạt động, haha. Ngay bây giờ, nó hoạt động trong XML, nhưng cũng có thể thực hiện được cho các hình dạng trong Java. Nó khá phức tạp và tôi tưởng tượng có một cách để đơn giản hóa nó thành một hình dạng duy nhất, nhưng đây là những gì tôi có bây giờ:

green_horizontal_gradient.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle"
    >
    <corners
        android:radius="3dp"
        />
    <gradient
        android:angle="0"
        android:startColor="#FF63a34a"
        android:endColor="#FF477b36"
        android:type="linear"
        />    
</shape>

Half_overlay.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle"
    >
    <solid
        android:color="#40000000"
        />
</shape>

layer_list.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <item
        android:drawable="@drawable/green_horizontal_gradient"
        android:id="@+id/green_gradient"
        />
    <item
        android:drawable="@drawable/half_overlay"
        android:id="@+id/half_overlay"
        android:top="50dp"
        />
</layer-list>

test.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    >
    <TextView
        android:id="@+id/image_test"
        android:background="@drawable/layer_list"
        android:layout_width="fill_parent"
        android:layout_height="100dp"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:gravity="center"
        android:text="Layer List Drawable!"
        android:textColor="@android:color/white"
        android:textStyle="bold"
        android:textSize="26sp"     
        />
</RelativeLayout>

Được rồi, về cơ bản, tôi đã tạo một gradient hình dạng trong XML cho gradient xanh lục ngang, được đặt ở góc 0 độ, đi từ màu xanh lục bên trái của khu vực trên cùng, sang màu xanh lục bên phải. Tiếp theo, tôi tạo một hình chữ nhật với một nửa màu xám trong suốt. Tôi khá chắc chắn rằng có thể được đưa vào XML trong danh sách lớp, làm mờ tệp bổ sung này, nhưng tôi không biết làm thế nào. Nhưng không sao, sau đó loại phần hacky xuất hiện trong tệp XML layer_list. Tôi đặt gradient màu xanh lá cây làm lớp dưới cùng, sau đó đặt lớp phủ một nửa làm lớp thứ hai, bù lại từ trên xuống 50dp. Tuy nhiên, rõ ràng bạn muốn con số này luôn bằng một nửa dù kích thước chế độ xem của bạn là bao nhiêu và không phải là 50dp cố định. Tôi không nghĩ rằng bạn có thể sử dụng tỷ lệ phần trăm, mặc dù. Từ đó, tôi chỉ cần chèn một TextView vào bố cục test.xml của mình, sử dụng tệp layer_list.xml làm nền.

văn bản thay thế

Tada!

Thêm một chỉnh sửa : Tôi đã nhận ra rằng bạn chỉ có thể nhúng các hình dạng vào danh sách lớp có thể vẽ dưới dạng các mục, nghĩa là bạn không cần 3 tệp XML riêng biệt nữa! Bạn có thể đạt được kết quả tương tự khi kết hợp chúng như vậy:

layer_list.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <item>
        <shape
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle"
            >
            <corners
                android:radius="3dp"
                />
            <gradient
                android:angle="0"
                android:startColor="#FF63a34a"
                android:endColor="#FF477b36"
                android:type="linear"
                />    
        </shape>
    </item>
    <item
        android:top="50dp" 
        >
        <shape
            android:shape="rectangle"
            >
            <solid
                android:color="#40000000"
                />
        </shape>            
    </item>
</layer-list>

Bạn có thể layer nhiều mặt hàng như bạn muốn theo cách này! Tôi có thể thử chơi xung quanh và xem liệu tôi có thể có được kết quả linh hoạt hơn thông qua Java không.

Tôi nghĩ rằng đây là lần chỉnh sửa cuối cùng ... : Được rồi, vì vậy bạn chắc chắn có thể sửa lỗi định vị thông qua Java, như sau:

    TextView tv = (TextView)findViewById(R.id.image_test);
    LayerDrawable ld = (LayerDrawable)tv.getBackground();
    int topInset = tv.getHeight() / 2 ; //does not work!
    ld.setLayerInset(1, 0, topInset, 0, 0);
    tv.setBackgroundDrawable(ld);

Tuy nhiên! Điều này dẫn đến một vấn đề khó chịu khác là bạn không thể đo TextView cho đến khi nó được rút ra. Tôi không chắc chắn làm thế nào bạn có thể thực hiện điều này ... nhưng chèn thủ công một số cho topInset không hoạt động.

Tôi nói dối, thêm một lần chỉnh sửa

Được rồi, tìm ra cách cập nhật thủ công lớp này có thể vẽ được để phù hợp với chiều cao của container, mô tả đầy đủ có thể được tìm thấy ở đây . Mã này sẽ đi theo phương thức onCreate () của bạn:

final TextView tv = (TextView)findViewById(R.id.image_test);
        ViewTreeObserver vto = tv.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                LayerDrawable ld = (LayerDrawable)tv.getBackground();
                ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
            }
        });

Và tôi đã hoàn thành! Phù! :)


Vấn đề với điều này là độ dốc dọc. Tôi cần hình dạng được phân tách theo chiều dọc (nghĩa là: một đường ngang), nhưng tôi cần độ dốc theo chiều ngang (nghĩa là: di chuyển sang trái sang phải). Với ví dụ này, cả dải phân cách và độ dốc đều thẳng đứng.
Andrew

Oh okay, tôi hiểu những gì bạn đang nói bây giờ. (Hình ảnh bị chặn tại nơi làm việc, vì vậy tôi chỉ có thể xem ảnh của bạn từ điện thoại của mình, khó có thể nói chi tiết tốt). Thật không may, tôi không có giải pháp cho việc đó, mặc dù tôi cho rằng bạn có thể tạo một hình dạng có thể vẽ được với hai hình chữ nhật, một hình chữ nhật nằm ngang, một hình có độ dốc dọc từ trắng sang đen với một nửa kích thước ở độ mờ một nửa, thẳng hàng với đáy. Theo như cách làm mà tôi không có bất kỳ ví dụ cụ thể nào vào lúc này.
Kevin Coppock

Phải, đó là điều tôi đã hy vọng có thể làm được, nhưng tôi vẫn chưa tìm ra. Tôi đánh giá cao sự giúp đỡ của bạn
Andrew

Không có gì! Tôi đã thử thêm một lần nữa và tôi nghĩ rằng tôi đã có khá nhiều thứ bạn đang tìm kiếm. Bạn có thể phải thực hiện những điều này một cách linh hoạt thông qua Java (theo nhận xét) nhưng điều này thực hiện thủ thuật cho các kích thước cố định ngay bây giờ.
Kevin Coppock

17
Đó là loại câu trả lời bạn muốn đưa ra ít nhất là 10. Tôi thích cảm giác chiến đấu trong câu trả lời của bạn: bạn so với API Android, ai sẽ thắng? Liệu chúng ta có bao giờ biết? ;) Ngoài ra, đó là tài liệu học tập siêu hữu ích vì bạn đã giữ lại tất cả những điều kỳ quặc mà bạn gặp phải.
andr

28

Bạn có thể lớp hình dạng gradient trong xml bằng cách sử dụng danh sách lớp. Hãy tưởng tượng một nút có trạng thái mặc định như bên dưới, trong đó mục thứ hai là bán trong suốt. Nó thêm một loại họa tiết. (Vui lòng loại trừ các màu được xác định tùy chỉnh.)

<!-- Normal state. -->
<item>
    <layer-list>
        <item>  
            <shape>
                <gradient 
                    android:startColor="@color/grey_light"
                    android:endColor="@color/grey_dark"
                    android:type="linear"
                    android:angle="270"
                    android:centerColor="@color/grey_mediumtodark" />
                <stroke
                    android:width="1dp"
                    android:color="@color/grey_dark" />
                <corners
                    android:radius="5dp" />
            </shape>
        </item>
        <item>  
            <shape>
                <gradient 
                    android:startColor="#00666666"
                    android:endColor="#77666666"
                    android:type="radial"
                    android:gradientRadius="200"
                    android:centerColor="#00666666"
                    android:centerX="0.5"
                    android:centerY="0" />
                <stroke
                    android:width="1dp"
                    android:color="@color/grey_dark" />
                <corners
                    android:radius="5dp" />
            </shape>
        </item>
    </layer-list>
</item>


4

Bạn CÓ THỂ làm điều đó bằng cách chỉ sử dụng các hình dạng xml - chỉ cần sử dụng danh sách lớp VÀ phần đệm âm như thế này:

    <layer-list>

        <item>
            <shape>
                <solid android:color="#ffffff" />

                <padding android:top="20dp" />
            </shape>
        </item>

        <item>
            <shape>
                <gradient android:endColor="#ffffff" android:startColor="#efefef" android:type="linear" android:angle="90" />

                <padding android:top="-20dp" />
            </shape>
        </item>

    </layer-list>

1

Hãy thử phương pháp này sau đó bạn có thể làm mọi thứ bạn muốn.
Nó giống như một chồng, vì vậy hãy cẩn thận mặt hàng nào đến trước hay cuối cùng.

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:right="50dp" android:start="10dp" android:left="10dp">
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <corners android:radius="3dp" />
        <solid android:color="#012d08"/>
    </shape>
</item>
<item android:top="50dp">
    <shape android:shape="rectangle">
        <solid android:color="#7c4b4b" />
    </shape>
</item>
<item android:top="90dp" android:end="60dp">
    <shape android:shape="rectangle">
        <solid android:color="#e2cc2626" />
    </shape>
</item>
<item android:start="50dp" android:bottom="20dp" android:top="120dp">
    <shape android:shape="rectangle">
        <solid android:color="#360e0e" />
    </shape>
</item>


0

Bạn đã thử phủ một độ dốc với độ mờ gần như trong suốt để làm nổi bật lên trên một hình ảnh khác có độ mờ đục cho độ dốc màu xanh lá cây chưa?


Không, tôi không có, mặc dù tôi không chắc chắn làm thế nào để đặt một gradient trên một hình khác. Tôi đang sử dụng hình dạng làm nền của linearLayout bằng android: background = "@ drawable / myshape"
Andrew

Có thể xếp hai hình dạng với các hình mờ khác nhau lên nhau không? (Tôi không biết.)
Greg D
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.