<b>Android</b><b>自定義元件(一)</b>
在原生元件上避免不了覆寫、組合等,以定義自己的元件,也友善以後複用。例如之前工程裡出現了多次的檔案浏覽器元件。
嗯~,該怎麼總結呢?
<b>一、概述</b>
自定義元件,大概可以這麼分吧。一、View或SurfaceView上自繪;二、ViewGroup布局子類整合;三、不清楚了~,好像也沒什麼好分的==。
本文的工程,個人覺着主要還是屬性資源的使用吧?工程主要例子介紹如下:
<b>名稱</b>
<b>效果</b>
<b>屬性</b>
Loading動态...的效果元件
loading...的動态效果
定義了如下四屬性:
1)loadImage:load字圖檔,reference類型
2)pointImage:小點圖檔,reference類型
3)pointCount:小點數目,integer類型
4)msecRate:毫秒級變化速率,integer類型
Title背景移位的效果元件
集合View布局,形成标題欄。實作了标題項下的背景移動的小效果。
定義了如下屬性:
1)titleLayout:标題欄布局
2)bgImage:item背景圖檔
3)bgLeftMargin:背景初始左邊距
4)animTime:移動動畫時間
ViewPager綁定标題的效果元件
ViewPager綁定标題欄,并實作了标題項下的背景移動的小效果。
效果特征如下:
1)背景随ViewPager滾動而同步在标題間滾動
2)點選标題時,ViewPager程式控制滾動&背景同步
屬性定義如下:
1)tLayout:标題欄布局
2)bImage:item背景圖檔
3)bMargin:背景初始左邊距
ListView增加抽屜的效果元件
ListView增加抽屜的效果元件。抽屜打開的界面隻用了一個。
1)listViewId:清單視圖id,reference類型
2)drawerContent:抽屜内容視圖id,reference類型
3)drawerClose:抽屜内容的關閉按鈕id,reference類型
自定義能隐藏更多标題的元件
集合View布局,形成标題欄。實作超過标題數限制時,自動顯示更多的效果。
初始化時,需要進行如下步驟:
1)設定顯示數限制,預設将為6。
2)綁定标題内容。為String[],将直接以TextView顯示==
3)綁定更多操作的視圖id。将自己加載,并為其設定點選事件。
4)綁定更多顯示的視圖。應為已有的ViewGroup。将自動加載超出限制的标題内容(TextView)。更多操作則将控制其顯示或隐藏。
另外,提供重新整理内容的方法,用于:一、标題欄内容的重新加載;二,更多顯示内容的重新加載。
自繪實時動态資料線
利用View繪制的實時資料顯示元件?
寫該文檔時才挪進來的了,感覺弄得亂亂的。
雙點縮放好像很不正确啊?應該是兩個觸摸點沒弄對,獲得的是一個手指頭觸發的兩個點,是以一下放大了。(猜測,總之我是不修了^^)
以下将以“ViewPager擴充元件”為例了,順便能看下ViewPager元件^^。
<b>二、步驟</b>
帶屬性資源,整合布局建構自定義元件的步驟~
<b>1</b><b>)attrs.xml</b>
定義元件需要用的屬性。不用的話,就相當于一個類為一個自定義元件,不和這些東西挂鈎。“自定義能隐藏更多标題的元件”即是這樣的,連屬性都沒定義==。
ViewPager擴充元件定義的内容:
<!-- TitleViewPager -->
<declare-styleable name="TitleViewPager">
<attr format="reference" name="tLayout" />
<attr format="reference" name="bImage" />
<attr format="integer" name="bMargin" />
</declare-styleable>
<b>2</b><b>)item.xml</b>
隻要是xml的resources标簽内即可,單獨弄出來呢最好。用以下方式定義一個id,用于View.setId(int id),主要用于相對布局時,相對于某個id的View什麼的。
<item name="containerLayout" type="id"/>
<b>3</b><b>)建立元件</b>
其類中引用屬性資源。并看下ViewPager的使用說明吧:OnPageChangeListener接口方法和PagerAdapter擴充卡内方法的注釋。<b></b>
public class TitleViewPager extends RelativeLayout implements
OnPageChangeListener, View.OnClickListener {
private Context mContext; // 上下文
private LayoutInflater mInflater; // 布局加載器
private int titleLayoutId; // 标題欄布局id
private int bgImageResId; // item背景圖檔資源id
private int bgLeftMargin; // 背景初始左邊距
private View titleLayout; // 标題欄布局
private ImageView mBgImage; // item背景圖檔
private ArrayList<View> mItemViews; // 标題項視圖集合
private ViewPager mViewPager; // ViewPager元件
private ArrayList<View> mPageViews; // 頁面視圖集合
private int prevOffset = -1; // 前次偏移值,這裡用了int值像素
private int currentIndex; // 目前頁面索引
private int previousIndex; // 前次頁面索引
private boolean isTitleClicked; // 标題項點選
private OnPageChangeListener mOnPageChangeListener; // 頁面變化監聽事件
// private final int REFRESH_RATE = 20; // 重新整理速率20msec
// private Scroller mScroller; // 滾動器
// private static final Interpolator sInterpolator = new Interpolator() {
// public float getInterpolation(float t) {
// t -= 1.0f;
// return t * t * t + 1.0f;
// }
// };
public TitleViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 獲得TypedArray對象
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.TitleViewPager);
// 擷取标題欄布局id,預設0
titleLayoutId = typedArray.getResourceId(
R.styleable.TitleViewPager_tLayout, 0);
// 擷取item背景圖檔資源id,預設0
bgImageResId = typedArray.getResourceId(
R.styleable.TitleViewPager_bImage, 0);
// 擷取背景初始左邊距,預設0
bgLeftMargin = typedArray.getInt(R.styleable.TitleViewPager_bMargin, 0);
initLayout(); // 初始化标題欄&ViewPager
mItemViews = new ArrayList<View>();
mPageViews = new ArrayList<View>();
}
// 初始化标題欄&ViewPager
private void initLayout() {
RelativeLayout containerLayout = new RelativeLayout(mContext); // 建立标題欄容器布局
containerLayout.setId(R.id.containerLayout); // 設定辨別符
LayoutParams containerParams = new LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); // 寬度WRAP_CONTENT,高度WRAP_CONTENT
containerParams.addRule(RelativeLayout.ALIGN_PARENT_TOP,
RelativeLayout.TRUE); // 貼于頂部
containerParams.addRule(RelativeLayout.CENTER_HORIZONTAL,
RelativeLayout.TRUE); // 水準居中
addView(containerLayout, containerParams); // 目前布局增加容器布局
if (0 != bgImageResId) {
mBgImage = new ImageView(mContext); // 建立item背景圖檔
mBgImage.setImageResource(bgImageResId); // 設定item背景圖檔
LayoutParams imageParams = new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); // 寬度WRAP_CONTENT,高度WRAP_CONTENT
imageParams.addRule(RelativeLayout.CENTER_VERTICAL,
RelativeLayout.TRUE); // 垂直居中
imageParams.leftMargin = bgLeftMargin; // 左邊距
containerLayout.addView(mBgImage, imageParams); // 标題欄容器增加标題item背景圖檔
}
if (titleLayoutId != 0) {
titleLayout = mInflater.inflate(titleLayoutId, this, false); // 獲得标題欄布局
LayoutParams titleParams = new LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); // 寬度WRAP_CONTENT,高度WRAP_CONTENT
titleParams.addRule(RelativeLayout.CENTER_HORIZONTAL,
RelativeLayout.TRUE); // 水準居中
titleParams.addRule(RelativeLayout.CENTER_VERTICAL,
containerLayout.addView(titleLayout, titleParams); // 标題欄容器增加标題欄布局
mViewPager = new ViewPager(mContext); // 建立ViewPager
mViewPager.setAdapter(new MPagerAdapter()); // 設定ViewPager擴充卡
mViewPager.setOnPageChangeListener(this); // 設定頁面改變的監聽接口
LayoutParams viewPagerParams = new LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); // 寬度FILL_PARENT,高度FILL_PARENT
viewPagerParams.addRule(RelativeLayout.BELOW, containerLayout.getId()); // 布于标題欄容器下方
viewPagerParams.addRule(RelativeLayout.CENTER_HORIZONTAL,
addView(mViewPager, viewPagerParams); // 目前布局增加容器ViewPager
// 增加一個綁定頁面
public void addBindedPage(int pageViewId, int titleItemId) {
mPageViews.add(mInflater.inflate(pageViewId, this, false));
View item = titleLayout.findViewById(titleItemId);
item.setOnClickListener(this);
mItemViews.add(item);
// 獲得頁面數量
public int getCount() {
return mPageViews.size();
// 初始化頁面(需要在UI加載完後,可以覆寫onWindowFocusChanged())
public void setPage(int index) {
setImagePosition(index); // 設定圖像至标題項位置
mViewPager.setCurrentItem(index, false); // 設定ViewPager目前頁面
// 設定目前頁面
public void setCurrentPage(int index, boolean isAnim) {
previousIndex = currentIndex; // 記錄前次頁面索引
currentIndex = index; // 設定目前頁面索引
mViewPager.setCurrentItem(index, isAnim); // 設定ViewPager目前頁面
// Title移動綁定在ViewPager的滾動事件内
// 設定圖像至标題項位置
private void setImagePosition(int index) {
if (null == mBgImage) {
return;
LayoutParams params = (RelativeLayout.LayoutParams) mBgImage
.getLayoutParams(); // 獲得圖檔布局
View item = mItemViews.get(index); // 标題項
// 注:UI加載完後getLeft()才有值
int targetLeftMargin = (int) (item.getLeft() + item.getWidth() / 2.0 - mBgImage
.getWidth() / 2.0); // 目标左邊距
// 位置未變時直接傳回,以避免多次setLayoutParams(...)
if (params.leftMargin == targetLeftMargin) {
params.leftMargin = targetLeftMargin;
mBgImage.setLayoutParams(params); // 使設定生效
// 設定圖像移動像素距離
private void moveImagePosition(int offset) {
params.leftMargin += offset;
/*
* 目前頁滾動時調用,無論是程式控制的平滑滾動還是使用者發起的觸摸滾動。
* arg0:第一個頁面目前顯示的位置索引。如果頁面偏移不是0,下一個頁面将會可見。
* arg1:表示第二個頁面位置偏移量的比例值,[0, 1)。(右側頁面所占螢幕百分比)
* arg2:表示第二個頁面位置偏移量的像素值。(右側頁面距右邊的像素值)
*/
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
// 判斷是否不在動畫,用positionOffsetPixels判斷判斷原因是:
// 1)position,在選中了頁面時就會改變,自動動畫時的不能判斷
// 2)positionOffset,動畫完變為0.0,但float不好直接等于判斷
// 3)positionOffsetPixels,動畫完變為0,int型^^
if (positionOffsetPixels == 0) {
setImagePosition(position);
prevOffset = -1;
isTitleClicked = false;
// 剛移動時,記錄下該次值
if (prevOffset == -1) {
prevOffset = positionOffsetPixels;
int pageOffset = positionOffsetPixels - prevOffset; // 頁面偏移距離
prevOffset = positionOffsetPixels;
if (null != mBgImage) {
try {
if (pageOffset < 0) { // 左->右
int prevIndex, nextIndex;
if (isTitleClicked) {
prevIndex = previousIndex;
nextIndex = currentIndex;
} else {
prevIndex = currentIndex;
nextIndex = currentIndex - 1;
}
// 兩菜單項間的距離
int itemDistance = mItemViews.get(prevIndex).getLeft()
- mItemViews.get(nextIndex).getLeft();
// 圖檔偏移距離
int imageOffset = pageOffset * itemDistance
/ mViewPager.getWidth();
// 設定圖像移動像素距離
moveImagePosition(imageOffset);
} else if (pageOffset > 0) { // 右->左
nextIndex = currentIndex + 1;
int itemDistance = mItemViews.get(nextIndex).getLeft()
- mItemViews.get(prevIndex).getLeft();
}
} catch (IndexOutOfBoundsException e) {
// 類似在中間左右左右的來回拖拽==,判斷還不夠啊T^T
setImagePosition(currentIndex);
isTitleClicked = false;
}
if (null != mOnPageChangeListener) {
mOnPageChangeListener.onPageScrolled(position, positionOffset,
positionOffsetPixels);
* 當一個新頁面被選中時被調用。動畫不一定必須完成。
* arg0:新選中頁面的位置索引
public void onPageSelected(int position) {
mOnPageChangeListener.onPageSelected(position);
* 滾動狀态改變時調用。用于發現使用者何時開始拖動、頁面何時自動沉降到目前頁(使用者不拖動時)、或者何時完全停止/空閑。
* arg0:新的滾動狀态。SCROLL_STATE_DRAGGING、SCROLL_STATE_SETTLING、SCROLL_STATE_IDLE
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_DRAGGING) { // 使用者拖動時
mOnPageChangeListener.onPageScrollStateChanged(state);
// 自定義的ViewPager擴充卡
private class MPagerAdapter extends PagerAdapter {
/*
* 移除一個給定位置的頁面。擴充卡有責任從它的容器中移除視圖,雖然這僅必須确認動作是在finishUpdate()後按時間完成的。
* arg0:容器視圖,從中将移除頁面。
* arg1:移除的頁面位置
* arg2:和instantiateItem(View, int)傳回的一樣的對象
*/
@Override
public void destroyItem(View arg0, int arg1, Object arg2) {
((ViewPager) arg0).removeView(mPageViews.get(arg1));
* 當顯示的界面完成變化後調用。在這裡,你應當必須確定所有的頁面已經真正的從容器中增加或删除。
* arg0:容器視圖,用于顯示擴充卡的頁面視圖
public void finishUpdate(View arg0) {
// 傳回可用界面的數量
public int getCount() {
return mPageViews.size();
* 建立一個給定位置的界面。擴充卡有責任給這邊給出的容器增加一個視圖,雖然這僅必須确認動作是在finishUpdate()後按時間完成的。
* arg0:容器視圖,在裡面将顯示頁面。
* arg1:要被裝載的頁面位置
* Object:傳回一個展現新畫面的對象。這不必須是一個View,也可以是一些其他的頁面容器。
public Object instantiateItem(View arg0, int arg1) {
((ViewPager) arg0).addView(mPageViews.get(arg1), 0);
return mPageViews.get(arg1);
// 是否是由對象生成的視圖
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == (arg1);
// 恢複狀态
public void restoreState(Parcelable arg0, ClassLoader arg1) {
// 儲存狀态,傳回序列化對象
public Parcelable saveState() {
return null;
* 當顯示的頁面将要開始變化時調用
public void startUpdate(View arg0) {
public void onClick(View v) {
int size = mItemViews.size(); // 大小
for (int i = 0; i < size; i++) {
if (mItemViews.get(i).getId() == v.getId()) {
isTitleClicked = true;
setCurrentPage(i, true);
break;
// 獲得标題項視圖集合
public ArrayList<View> getItemViews() {
return mItemViews;
// 獲得 頁面視圖集合
public ArrayList<View> getPageViews() {
return mPageViews;
// 設定頁面變化監聽事件
public void setOnPageChangeListener(OnPageChangeListener listener) {
mOnPageChangeListener = listener;
}
<a target="_blank" href="http://vaero.blog.51cto.com/4350852/872763"> <b>Android</b><b>自定義元件(二)</b></a>
附件工程,見(二)。
本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/872734,如需轉載請自行聯系原作者