1. 程式人生 > >Android自定義控制元件系列案例【一】

Android自定義控制元件系列案例【一】

Android自定義控制元件的重要性就不多說了,總之是技術進階,面試常見,高薪必備。

本篇博文的目的很簡單,就是希望通過自定義控制元件來解決一個常見需求點,從而感受一下自定義控制元件的魅力與強大。

案例描述:

有一張正方形圖片,希望在螢幕上還以正方形,並且不會出現變形,與此同時,希望圖片的寬正好是螢幕的寬。

案例效果:

       

案例實現:

方式一: 使用ImageView來顯示圖片

要顯示一張圖片,首先我們就會想到使用Android自身的控制元件ImageView。而需求點要求圖片顯示的寬要和螢幕的寬一樣,所以ImageView的寬我們自然使用android:layout_width="match_parent",這樣顯示的圖片寬就會隨著裝置螢幕的寬自適應。寬確定了之後,我們來確定高,因為圖片的高我們佈局的時候不一定知道,所以不能定死了(比如100dp,200dp等),那這樣我們就只有兩個選擇,一是填充父佈局,讓ImageView的高與父佈局一樣高( android:layout_height="match_parent"),二是包裹內容,讓ImageView的高與圖片一樣高,(android:layout_height="wrap_content"),第二種理論上應該是我們想要的,接下來我們看看分別使用match_parent和wrap_content確定高之後的效果:

顯示這兩種都有變形的情況,且math_parent肯定不行,而wrap_content時,雖有變形,但是這個時候肯定有人想到ImageView控制元件有個屬性scaleType,它可以對ImageView上要顯示的圖片進行縮放,裁剪等操作。下面是一個使用此屬性的效果:  (1)android:scaleType="matrix" 縮放型別為matrix時,圖片無變形,但顯示寬度與裝置寬不一致。
(2)android:scaleType="fitXY" 縮放型別為fitXY時,圖片寬雖與裝置寬一致,但水平拉伸了。 (3)android:scaleType="fitStart"
縮放型別為fitStart
時,圖片無變形,但顯示寬度與裝置寬不一致。
(4)android:scaleType="fitCenter"
縮放型別為fitCenter時,圖片無變形,但顯示寬度與裝置寬不一致。
(5)android:scaleType="fitEnd"
縮放型別為fitEnd時,圖片無變形,但顯示寬度與裝置寬不一致。
(6)android:scaleType="Center"
縮放型別為Center時,圖片無變形,但顯示寬度與裝置寬不一致。
(7)android:scaleType="CenterCrop"
縮放型別為CenterCrop時,無變形,寬一致,但上下部分被裁剪了。
(8)android:scaleType="CenterInside
"

縮放型別為CenterInside時,圖片無變形,但顯示寬度與裝置寬不一致。
上面把ImageView的ScaleType中的所有情況都測試了一遍,都不能滿足我們的需求,在這種情況下,我們不得不放棄使用Android原生態控制元件來滿足需求了,這也就正是Android自定控制元件要解決的問題。

方式二:使用自定義控制元件來顯示圖片

自定義控制元件有完全自定義、組合自定義、基於原生態控制元件自定義這幾種常用的方式,分析我們的需求本質問題還是要顯示一張圖片,所以我們採用基於原生態顯示圖片的控制元件ImageView進行定製,從方式一我們發現,使用ImageView不能實現不變形且圖片寬與螢幕寬一致的統一,所以自定義控制元件的目的就是要解決這個統一問題。明白了自定義控制元件我解決的問題,接下來就來進行定製。 (1)首先建立一個類MyImageView,繼承於ImageView:
package com.kedi.mylayout;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

public class MyImageView extends ImageView{

	

}

(2)重寫父類構造方法: 一般需要實現三個(一參、二參、三參),並且為了方便管理構造方法,我們改一參構造方法呼叫二參,二參調三參,然後如果有初始化邏輯,我們統一寫在三參構造方法中。
package com.kedi.mylayout;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;

public class MyImageView extends ImageView {
	public MyImageView(Context context) {
		this(context,null);	
		
	}
	public MyImageView(Context context, AttributeSet attrs) {
		this(context,attrs,0);
	}
	public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs,defStyleAttr);
	}	

}

(3)實現自定義控制元件要解決的問題:        接下來就是寫自定義控制元件要解決的問題了,根據要解決的問題可以需要重寫的方法不一樣,比如onMeasure()、onLayout()、onDraw()、onFinishInflate()、onSizeChanged()、onInterceptTouchEvent()、onTouchEvent()等。就當前需求而言,我們要解決的是圖片顯示的寬高之類問題,所以需要重寫onMeasure()方法,這個方法是由佈局測量寬高大小並建議子控制元件使用,用來確定或修改子控制元件的大小。
package com.kedi.mylayout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MyImageView extends ImageView{
	public MyImageView(Context context) {
		this(context,null);
		
		
	}
	public MyImageView(Context context, AttributeSet attrs) {
		this(context,attrs,0);
	}
	public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs,defStyleAttr);
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		
	}

	

}
為了讓自定義控制元件的寬高一樣,並且寬要和裝置寬一致,我們可以讓它的父佈局寬高為march_parent,讓父佈局寬適應裝置的寬,這樣在onMeasure()方法中,父佈局就會推薦自定義控制元件的寬為父佈局的寬,也就是裝置的寬,然後我們再通過setMeasuredDimension(width, width);方法,讓自定義控制元件的寬高一樣。從而實現了要解決的問題。

package com.kedi.mylayout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.RelativeLayout;
/**
 * 自定義控制元件
 * @author 張科勇
 *
 */
public class MyImageView extends RelativeLayout {
	public MyImageView(Context context) {
		this(context,null);
		
		
	}
	public MyImageView(Context context, AttributeSet attrs) {
		this(context,attrs,0);
	}
	public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs,defStyleAttr);
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	<span style="color:#ff0000;">	//獲取父佈局推薦的寬高,並把小者作為自定義控制元件的寬高
		int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
		//將自定義控制元件的寬高設定成父佈局推薦的寬
		setMeasuredDimension(width, width);</span>
	}

	

}

(4)使用自定義控制元件佈局: 使用自定控制元件佈局與Android原生態控制元件佈局沒什麼區別,唯一需要注意的是自定義控制元件在使用的時候需要加類全名(包名+類名)。需要注意的是自己在使用自定控制元件佈局的時候記得把這個包名換成自己的包名和類名。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <span style="color:#ff0000;"><com.kedi.mylayout.MyImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:background="@drawable/pic" >
    </com.kedi.mylayout.MyImageView></span>

</RelativeLayout>
執行程式,效果如下圖: 我們可以看到使用自定義控制元件確定解決了使用方式一時出現的變形和寬高問題,這就是自定義控制元件的強大和靈活。但自定控制元件容易入門,難於精通,需要我們不斷積累才能夠運用自如。  讀完本篇,可能會有點感覺,但如想進一步學習自定義佈局,自定義屬性的案例可以閱讀