一、发现奇怪的问题?
在研究Android Drawable资源的时候,发现了一个奇怪的问题。在官方API介绍中:
ShapeDrawable 介绍:This object can be defined in an XML file with the <shape>
element(这个对象可以用<shape>元素在xml文件中定义)
GradientDrawable 介绍:This object can be defined in an XML file with the <shape>
element(这个对象可以用<shape>元素在xml文件中定义)
两者的介绍一模一样,都说可以使用<shape>标签在xml文件中定义。
那么,到底用<shape>标签定义的是什么的呢?经过下面的实验:
在shape.xml中定义:
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <gradient
- android:startColor="#FFFF0000"
- android:endColor="#80FF00FF"
- android:angle="45"/>
- <padding android:left="7dp"
- android:top="7dp"
- android:right="7dp"
- android:bottom="7dp" />
- <corners android:radius="8dp" />
- </shape>
在layout文件中使用:
- <TextView
- android:id="@+id/textView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/gradient_box"
- android:text="测试" />
在java代码中控制:
- TextView textView=(TextView)findViewById(R.id.textView);
- ShapeDrawable gradientDrawable=(ShapeDrawable)textView.getBackground();
- :java.lang.ClassCastException: android.graphics.drawable.GradientDrawable
可见,使用<shape>标签定义的是GradientDrawable。
并且,在官方API Drawable Resource中ShapeDrawable介绍到:
- compiled resource datatype:
- Resource pointer to a GradientDrawable.
指向GradientDrawable的指针。
可见,编译的类型是GradientDrawable。
那么,ShapeDrawable是怎么定义的,找了网上的资料,结果硬是没找到如何在XML文件中定义它,只能通过代码的方式实现。如何使用它在后面再介绍。
二、使用GradientDrawable
GradientDrawable的作用在于定于各种样式的渐变。在XML文件中使用<shape>元素定义。-
文件位置:
-
res/drawable/filename.xml
文件名即资源ID
编译资源类型:
-
指向
GradientDrawable
.
资源引用
-
In Java:
R.drawable.filename
In XML:@[package:]drawable/filename
语法
-
<span class="pun"><?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> encoding</span><span class="pun">=</span><span class="str">"utf-8"</span><span class="pun">?></span><span class="pln"> </span><span class="tag"><</span><a target=_blank style="color: rgb(202, 0, 0);">shape</a><span class="pln"> </span><span class="atn">xmlns:android</span><span class="pun">=</span><span class="atv">"http://schemas.android.com/apk/res/android"</span><span class="pln"> </span><span class="atn">android:shape</span><span class="pun">=</span><span class="atv">["rectangle"</span><span class="pln"> | </span><span class="atv">"oval"</span><span class="pln"> | </span><span class="atv">"line"</span><span class="pln"> | </span><span class="atv">"ring"</span><span class="pln">] </span><span class="tag">></span><span class="pln"> </span><span class="tag"><</span><a target=_blank style="color: rgb(202, 0, 0);">corners</a><span class="pln"> </span><span class="atn">android:radius</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:topLeftRadius</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:topRightRadius</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:bottomLeftRadius</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:bottomRightRadius</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="tag">/></span><span class="pln"> </span><span class="tag"><</span><a target=_blank style="color: rgb(202, 0, 0);">gradient</a><span class="pln"> </span><span class="atn">android:angle</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:centerX</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:centerY</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:centerColor</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:endColor</span><span class="pun">=</span><span class="atv">"</span><em>color</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:gradientRadius</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:startColor</span><span class="pun">=</span><span class="atv">"</span><em>color</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:type</span><span class="pun">=</span><span class="atv">["linear"</span><span class="pln"> | </span><span class="atv">"radial"</span><span class="pln"> | </span><span class="atv">"sweep"</span><span class="pln">] </span><span class="atn">android:useLevel</span><span class="pun">=</span><span class="atv">["true"</span><span class="pln"> | </span><span class="atv">"false"</span><span class="pln">] </span><span class="tag">/></span><span class="pln"> </span><span class="tag"><</span><a target=_blank style="color: rgb(202, 0, 0);">padding</a><span class="pln"> </span><span class="atn">android:left</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:top</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:right</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:bottom</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="tag">/></span><span class="pln"> </span><span class="tag"><</span><a target=_blank style="color: rgb(202, 0, 0);">size</a><span class="pln"> </span><span class="atn">android:width</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:height</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="tag">/></span><span class="pln"> </span><span class="tag"><</span><a target=_blank style="color: rgb(202, 0, 0);">solid</a><span class="pln"> </span><span class="atn">android:color</span><span class="pun">=</span><span class="atv">"</span><em>color</em><span class="atv">"</span><span class="pln"> </span><span class="tag">/></span><span class="pln"> </span><span class="tag"><</span><a target=_blank style="color: rgb(202, 0, 0);">stroke</a><span class="pln"> </span><span class="atn">android:width</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:color</span><span class="pun">=</span><span class="atv">"</span><em>color</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:dashWidth</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="atn">android:dashGap</span><span class="pun">=</span><span class="atv">"</span><em>integer</em><span class="atv">"</span><span class="pln"> </span><span class="tag">/></span><span class="pln"> </span><span class="tag"></shape></span>
元素:
-
<padding>
-
内容与视图边界的距离
示例:
-
XML file saved at
res/drawable/gradient_box.xml
:<span class="pun"><?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> encoding</span><span class="pun">=</span><span class="str">"utf-8"</span><span class="pun">?></span><span class="pln"> </span><span class="tag"><shape</span><span class="pln"> </span><span class="atn">xmlns:android</span><span class="pun">=</span><span class="atv">"http://schemas.android.com/apk/res/android"</span><span class="pln"> </span><span class="atn">android:shape</span><span class="pun">=</span><span class="atv">"rectangle"</span><span class="tag">></span><span class="pln"> </span><span class="tag"><gradient</span><span class="pln"> </span><span class="atn">android:startColor</span><span class="pun">=</span><span class="atv">"#FFFF0000"</span><span class="pln"> </span><span class="atn">android:endColor</span><span class="pun">=</span><span class="atv">"#80FF00FF"</span><span class="pln"> </span><span class="atn">android:angle</span><span class="pun">=</span><span class="atv">"45"</span><span class="tag">/></span><span class="pln"> </span><span class="tag"><padding</span><span class="pln"> </span><span class="atn">android:left</span><span class="pun">=</span><span class="atv">"7dp"</span><span class="pln"> </span><span class="atn">android:top</span><span class="pun">=</span><span class="atv">"7dp"</span><span class="pln"> </span><span class="atn">android:right</span><span class="pun">=</span><span class="atv">"7dp"</span><span class="pln"> </span><span class="atn">android:bottom</span><span class="pun">=</span><span class="atv">"7dp"</span><span class="pln"> </span><span class="tag">/></span><span class="pln"> </span><span class="tag"><corners</span><span class="pln"> </span><span class="atn">android:radius</span><span class="pun">=</span><span class="atv">"8dp"</span><span class="pln"> </span><span class="tag">/></span><span class="pln"> </span><span class="tag"></shape></span>
This layout XML applies the shape drawable to a View:
<span class="tag"><TextView</span><span class="pln"> </span><span class="atn">android:background</span><span class="pun">=</span><span class="atv">"@drawable/gradient_box"</span><span class="pln"> </span><span class="atn">android:layout_height</span><span class="pun">=</span><span class="atv">"wrap_content"</span><span class="pln"> </span><span class="atn">android:layout_width</span><span class="pun">=</span><span class="atv">"wrap_content"</span><span class="pln"> </span><span class="tag">/></span>
This application code gets the shape drawable and applies it to a View:
<span class="typ">Resources</span><span class="pln"> res </span><span class="pun">=</span><span class="pln"> </span><code><a target=_blank style="color: rgb(202, 0, 0);"><span class="pln">getResources</span><span class="pun">()</span></a></code><span class="pun">;</span><span class="pln"> </span><span class="typ">Drawable</span><span class="pln"> shape </span><span class="pun">=</span><span class="pln"> res</span><span class="pun">.</span><span class="pln"> </span><code><a target=_blank style="color: rgb(202, 0, 0);">getDrawable</a></code><span class="pun">(</span><span class="pln">R</span><span class="pun">.</span><span class="pln">drawable</span><span class="pun">.</span><span class="pln">gradient_box</span><span class="pun">);</span><span class="pln"> </span><span class="typ">TextView</span><span class="pln"> tv </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">TextView</span><span class="pun">)</span><span class="pln">findViewByID</span><span class="pun">(</span><span class="pln">R</span><span class="pun">.</span><span class="pln">id</span><span class="pun">.</span><span class="pln">textview</span><span class="pun">);</span><span class="pln"> tv</span><span class="pun">.</span><span class="pln">setBackground</span><span class="pun">(</span><span class="pln">shape</span><span class="pun">);</span>
参考:
下面是API的Demo:
- package com.example.shapedrawable;
- import android.app.Activity;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.Path;
- import android.graphics.Picture;
- import android.graphics.Rect;
- import android.graphics.drawable.Drawable;
- import android.graphics.drawable.GradientDrawable;
- import android.graphics.drawable.ShapeDrawable;
- import android.os.Bundle;
- import android.util.AttributeSet;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.ViewParent;
- import android.widget.TextView;
- public class MainActivity extends GraphicsActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(new SampleView(this));
- }
- private static class SampleView extends View {
- private Path mPath;
- private Paint mPaint;
- private Rect mRect;
- private GradientDrawable mDrawable;
- public SampleView(Context context) {
- super(context);
- setFocusable(true);
- mPath = new Path();
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mRect = new Rect(0, 0, 120, 120);
- mDrawable = new GradientDrawable(
- GradientDrawable.Orientation.TL_BR, new int[] { 0xFFFF0000,
- 0xFF00FF00, 0xFF0000FF });
- mDrawable.setShape(GradientDrawable.RECTANGLE);
- mDrawable.setGradientRadius((float) (Math.sqrt(2) * 60));
- }
- static void setCornerRadii(GradientDrawable drawable, float r0,
- float r1, float r2, float r3) {
- drawable.setCornerRadii(new float[] { r0, r0, r1, r1, r2, r2, r3,
- r3 });
- }
- //重点的绘制过程
- @Override
- protected void onDraw(Canvas canvas) {
- mDrawable.setBounds(mRect);
- float r = 16;
- canvas.save();
- canvas.translate(10, 10);
- mDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
- setCornerRadii(mDrawable, r, r, 0, 0);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.save();
- canvas.translate(10 + mRect.width() + 10, 10);
- mDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);
- setCornerRadii(mDrawable, 0, 0, r, r);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.translate(0, mRect.height() + 10);
- canvas.save();
- canvas.translate(10, 10);
- mDrawable.setGradientType(GradientDrawable.SWEEP_GRADIENT);
- setCornerRadii(mDrawable, 0, r, r, 0);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.save();
- canvas.translate(10 + mRect.width() + 10, 10);
- mDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
- setCornerRadii(mDrawable, r, 0, 0, r);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.translate(0, mRect.height() + 10);
- canvas.save();
- canvas.translate(10, 10);
- mDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);
- setCornerRadii(mDrawable, r, 0, r, 0);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.save();
- canvas.translate(10 + mRect.width() + 10, 10);
- mDrawable.setGradientType(GradientDrawable.SWEEP_GRADIENT);
- setCornerRadii(mDrawable, 0, r, 0, r);
- mDrawable.draw(canvas);
- canvas.restore();
- }
- }
- }
- class GraphicsActivity extends Activity {
- // set to true to test Picture
- private static final boolean TEST_PICTURE = false;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
- @Override
- public void setContentView(View view) {
- if (TEST_PICTURE) {
- ViewGroup vg = new PictureLayout(this);
- vg.addView(view);
- view = vg;
- }
- super.setContentView(view);
- }
- }
- class PictureLayout extends ViewGroup {
- private final Picture mPicture = new Picture();
- public PictureLayout(Context context) {
- super(context);
- }
- public PictureLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- @Override
- public void addView(View child) {
- if (getChildCount() > 1) {
- throw new IllegalStateException(
- "PictureLayout can host only one direct child");
- }
- super.addView(child);
- }
- @Override
- public void addView(View child, int index) {
- if (getChildCount() > 1) {
- throw new IllegalStateException(
- "PictureLayout can host only one direct child");
- }
- super.addView(child, index);
- }
- @Override
- public void addView(View child, LayoutParams params) {
- if (getChildCount() > 1) {
- throw new IllegalStateException(
- "PictureLayout can host only one direct child");
- }
- super.addView(child, params);
- }
- @Override
- public void addView(View child, int index, LayoutParams params) {
- if (getChildCount() > 1) {
- throw new IllegalStateException(
- "PictureLayout can host only one direct child");
- }
- super.addView(child, index, params);
- }
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int count = getChildCount();
- int maxHeight = 0;
- int maxWidth = 0;
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- measureChild(child, widthMeasureSpec, heightMeasureSpec);
- }
- }
- maxWidth += getPaddingLeft() + getPaddingRight();
- maxHeight += getPaddingTop() + getPaddingBottom();
- Drawable drawable = getBackground();
- if (drawable != null) {
- maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
- maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
- }
- setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
- resolveSize(maxHeight, heightMeasureSpec));
- }
- private void drawPict(Canvas canvas, int x, int y, int w, int h, float sx,
- float sy) {
- canvas.save();
- canvas.translate(x, y);
- canvas.clipRect(0, 0, w, h);
- canvas.scale(0.5f, 0.5f);
- canvas.scale(sx, sy, w, h);
- canvas.drawPicture(mPicture);
- canvas.restore();
- }
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight()));
- mPicture.endRecording();
- int x = getWidth() / 2;
- int y = getHeight() / 2;
- if (false) {
- canvas.drawPicture(mPicture);
- } else {
- drawPict(canvas, 0, 0, x, y, 1, 1);
- drawPict(canvas, x, 0, x, y, -1, 1);
- drawPict(canvas, 0, y, x, y, 1, -1);
- drawPict(canvas, x, y, x, y, -1, -1);
- }
- }
- @Override
- public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
- location[0] = getLeft();
- location[1] = getTop();
- dirty.set(0, 0, getWidth(), getHeight());
- return getParent();
- }
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final int count = super.getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final int childLeft = getPaddingLeft();
- final int childTop = getPaddingTop();
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- }
- }
- }
- }
三、ShapeDrawable的使用
ShapeDrawable没有发现如何在XML文件中定义使用,所以应该不属于Drawable Resource。这个留到后面学习Graphics在看。