Android組合控件

Android組合控件

組合控件,顧名思義,多個控件組合成一個控件使用。好比,咱們想要這樣一個ImageView,圖片的底部覆蓋一個浮層,浮層上面顯示一行文字,這個控件咱們能夠用TextView覆蓋在ImageView之上實現,咱們把這個控件命名爲「CoverImageView」吧。java

怎樣組合

既然是組合,那麼就須要一個容器把這些分散的控件裝在一塊兒,這個容器就是ViewGroup,如:LinearLayout、RelativeLayout等,因此組合控件都是繼承於一個ViewGroup的。android

組裝的方式有兩種:git

  1. xml文件裏定義好控件效果,而後經過LayoutInflater佈局到ViewGroup裏面;
  2. 直接在ViewGroup裏面經過代碼添加子控件,實現效果。

第一種方式可以比較直觀的看到組合效果,可是因爲須要解析xml,因此性能上稍微差點;第二種並不能直觀的看到效果,可是性能稍好。對於新手來講,能夠先用第一種方式實現,等到對組合控件的實現比較熟悉後可使用第二種方式實現。github

基本效果實現

能實現覆蓋效果的ViewGroup有RelativeLayout、FrameLayout,咱們以FrameLayout爲例。app

xml佈局以下:函數

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <!--圖片佔據整個控件大小-->
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:scaleType="centerCrop"
        android:src="@drawable/demo"/>

    <!--文字與控件等寬、在整個控件的底部-->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="#64000000"
        android:gravity="center"
        android:padding="6dp"
        android:text="@string/laomengzhu"
        android:textColor="#ffffff"/>
</FrameLayout>

效果以下:工具

base_preview

xml已經定義好了,咱們把它佈局到咱們的ViewGroup裏面去。咱們的控件「CoverImageView」繼承於FrameLayout,咱們實現一個「setupViews」函數,用來初始化View,並在全部構造函數裏面調用這個函數。佈局

public class CoverImageView extends FrameLayout {

    public CoverImageView(Context context) {
        super(context);
        setupViews(context, null);
    }

    public CoverImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupViews(context, attrs);
    }

    public CoverImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setupViews(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CoverImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setupViews(context, attrs);
    }

    private void setupViews(Context context, AttributeSet attrs) {
        //將xml佈局到ViewGroup裏面來
        View.inflate(context, R.layout.layout_civ, this);
    }
}

如今咱們能夠把這個控件放到咱們的Activity裏面看看效果了:性能

preview_in_activity1

佈局優化

前面咱們已經看到咱們想要的效果了,可是咱們在xml裏面的根佈局是FrameLayout,咱們的CoverImageView也是繼承於FrameLayout,那咱們最終實現的效果會不會套了兩層FrameLayout呢?優化

咱們可使用ADT裏面的Hierarchyviewer工具查看佈局層級關係,使用方法也很簡單:

  1. 在模擬器上運行你的程序(真機上我試了下不行);
  2. 打開Android Device Monitor,選中你程序的進程;
  3. 點擊右上角的樹形圖標,過一會咱們就能看到咱們當前頁面的層級關係了。

咱們當前頁面的部分層級關係以下圖:

preview_in_activity1

能夠看到CoverImage下面的確還有一層FrameLayout,這一層FrameLayout是毫無用處的,這樣的層級關係就會致使過分繪製問題(不懂的能夠百度一下,這裏就不贅述了),影響咱們的控件性能。

減小視圖層級,咱們可使用merge標籤,它能夠刪減多餘的層級,優化UI。咱們把咱們xml佈局裏面的FrameLayout換成merge,而後運行,看看視圖層級關係。

preview_in_activity1

層級減小了!!!

關於佈局優化,你們能夠看看這個博客:http://blog.csdn.net/xyz_lmn/article/details/14524567

經過代碼添加控件

前面講到,組裝控件的另外一個方式就是經過代碼添加控件,原理也很簡單,就是在咱們的ViewGroup裏面直接建立子控件、而後添加到咱們的ViewGroup裏,調整子控件的佈局,達到咱們想要的效果。因爲少了xml解析這一步(雖然你沒有解析,可是系統須要解析的),因此性能是優於第一種的。

private void setupViews(Context context, AttributeSet attrs) {
    //將xml佈局到ViewGroup裏面來
    /*View.inflate(context, R.layout.layout_civ, this);*/

    //經過代碼添加子控件
    LayoutParams lp;

    ImageView imageView = new ImageView(context);
    //設置縮放模式
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageView.setImageResource(R.drawable.demo);
    //設置子控件佈局參數
    lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    //將子控件加入ViewGroup裏
    addView(imageView, lp);

    TextView textView = new TextView(context);
    //設置內邊距
    int padding;
    if (isInEditMode()) {
        padding = 18;
    } else {
        padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6,
                getResources().getDisplayMetrics());
    }
    textView.setPadding(padding, padding, padding, padding);
    textView.setBackgroundColor(Color.parseColor("#64000000"));
    textView.setTextColor(Color.WHITE);
    //文字居中
    textView.setGravity(Gravity.CENTER);
    textView.setText(R.string.laomengzhu);
    lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    lp.gravity = Gravity.BOTTOM;
    addView(textView, lp);
}

效果和上面徹底一致。

自定義屬性

前面咱們實現了咱們想要的效果,可是咱們沒法修改控件裏面的圖片、文字大小,咱們須要定義控件的這些屬性,固然你還能夠定義文字顏色、文字背景色等屬性,咱們這裏只挑圖片、文字大小以做演示。

  1. 在res/values目錄下新建attrs.xml文件;
  2. 在attrs.xml裏面定義控件屬性;
  3. 在佈局中使用屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CoverImageView">
        <!--name:屬性名稱 format:屬性格式-->
        <!--屬性支持的格式有:
        1. reference:參考某一資源ID。
        2. color:顏色值。
        3. boolean:布爾值。
        4. dimension:尺寸值。
        5. float:浮點值。
        6. integer:整型值。
        7. string:字符串。
        8. fraction:百分數。
        9. enum:枚舉值。
        10. flag:位或運算。-->
        <attr name="imgSrc" format="reference"/>
        <attr name="coverTextSize" format="dimension|reference"/>
    </declare-styleable>
</resources>

而後咱們須要在控件中解析屬性:

//解析控件屬性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CoverImageView);

//解析圖片資源ID
int resId = ta.getResourceId(R.styleable.CoverImageView_imgSrc, -1);
if (resId != -1) {
    imageView.setImageResource(resId);
}

//解析文字大小
resId = ta.getResourceId(R.styleable.CoverImageView_coverTextSize, -1);
int textSize = 0;
if (resId != -1) {
    if (!isInEditMode()) {
        textSize = getResources().getDimensionPixelSize(resId);
    }
} else {
    textSize = ta.getDimensionPixelSize(R.styleable.CoverImageView_coverTextSize, 0);
}
if (textSize > 0) {
    textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}

//釋放
ta.recycle();

使用屬性:

<com.laomengzhu.civ.CoverImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/view"
    app:imgSrc="@drawable/demo1"
    app:coverTextSize="18sp"
    android:layout_centerInParent="true"/>

自定義控件的屬性默認是在xmlns:app="http://schemas.android.com/apk/res-auto"命名空間下的。

到此,咱們自定義組合控件就完成了,最終效果以下圖:

preview_in_activity1

代碼:https://github.com/laomengzhu/CustomWidget/tree/master/CombinationWidget/CoverImageView

相關文章
相關標籤/搜索