奔跑的肥猪

导航

Android中自定义控件

就像其他开发平台一样,android也可以自定义控件,如下所示,定义了四个自定义控件:翻页/menu/list/跳页

那自定义控件到底如何实现的呢?

一般是继承某个view,重写某些方法,然后再layout中引用即可。在实践过程发现如下三个问题:

1. 每个自定义控件都有特别的需求,比如跳页控件,点击button时button变黑,移开时又恢复原来状态。

有二种方法可以实现:

A:

1. 同在在layout 设置background属性,然后在drawable中实现它

<Button android:id="@+id/alert_bt_submit"
			android:layout_height="45px" android:layout_width="80px"
			android:text="@string/goto_page_title" android:layout_toRightOf="@id/alert_bt_clear"
			android:background="@drawable/btnchanger">
</Button>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
	<item android:state_focused="true" android:drawable="@drawable/im_smallimagelogo_click" />
	<item android:state_pressed="true" android:drawable="@drawable/im_smallimagelogo_click" />
	<item android:state_selected="true" android:drawable="@drawable/im_smallimagelogo_click" />
	<item android:drawable="@drawable/im_smallimagelogo" />
</selector>

效果如下:

2.你可以通过自定义控件,在layout.xml中引用它:

<com.fp.app.fpview.FPListTextView
    android:id="@+id/item_line_one" android:layout_below="@id/item_right_one"
style="@style/directoty_line" />
protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		if (textfocused) {
			Paint p = new Paint();
			p.setColor(Color.BLACK);
			Rect rct = new Rect();
			rct.set(this.getPaddingLeft() + MIDDLE_BORDER_OFFSET - 2, this.getHeight() - 2 - MIDDLE_BORDER_OFFSET, this.getWidth() - this.getPaddingLeft() - RIGHT_PADDING, this.getHeight() - MIDDLE_BORDER_OFFSET);
			Paint pnt = new Paint();
			pnt.setStyle(Paint.Style.FILL);
			pnt.setColor(Color.BLACK);
			canvas.drawRect(rct, pnt);
		} else {
			Paint p = new Paint();
			p.setColor(R.color.solid_grey);
			Rect rct = new Rect();
			rct.set(this.getPaddingLeft() + MIDDLE_BORDER_OFFSET - 2, this.getHeight() - 1 - MIDDLE_BORDER_OFFSET, this.getWidth() - this.getPaddingLeft() - RIGHT_PADDING, this.getHeight() - MIDDLE_BORDER_OFFSET);
			Paint pnt = new Paint();
			pnt.setStyle(Paint.Style.FILL);
			pnt.setColor(R.color.solid_grey);
			canvas.drawRect(rct, pnt);
		}
	}

	@Override
	protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
		textfocused = focused;
		this.invalidate();
	}

效果如下:

从看出focus从2位置到3位置,实际就是focus变化后invalidate调用ondraw实现的。发现2焦点lost后

调用onfocus事件, focused = false;3获得焦点后也调用onfocus,但是focus = true

3. 自定义控件还有一个好处,可以filter 一些狂点或者狂按操作,比如在menu菜单中,通过重写textview的

onkeydown 和 onkeyup方法,用户频繁按pad up/pad down就被过滤掉

@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		long currentKeyPressTime = SystemClock.uptimeMillis();
		if (currentKeyPressTime - mLastKeyPressTime < DEBOUNCE_TIME) {
			mLastKeyPressTime = currentKeyPressTime;
			return true;
		}

		mLastKeyPressTime = currentKeyPressTime;
		if (mIsKeyDisabled || (isWaitingForKeyUp && keyCode != KeyEvent.KEYCODE_BACK))
			return true;
		else {
			isWaitingForKeyUp = true;
		}

		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		isWaitingForKeyUp = false;
		super.onKeyUp(keyCode, event);
		return true;
	}

废话少说,下面贴一个翻页控件的例子:

1. 先自定义一个linearlayout

package com.fp.app.pager;

import com.fp.app.entry.R;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;

public class PagingComponent extends LinearLayout {

	private static final double RATIO_NEXTWIDTH = 0.2;
	private static final double RATIO_FIRSTWIDTH = 0.15;
	private static final double RATIO_CURRENTWIDTH = 0.14;
	private TextView tv_firstpage = null;
	private TextView tv_previouspage = null;
	private TextView tv_currentpage = null;
	private TextView tv_nextpage = null;
	private TextView tv_lastpage = null;

	public PagingComponent(Context context) {
		super(context);
		init();
	}

	public PagingComponent(Context context, AttributeSet aSet) {
		super(context, aSet);
		init();
	}

	private void init() {
		initLayoutInflater();
		int totalWidth = getTotalWidth();
	    initFirstAndLastPage(totalWidth);		
		initPreviousAndNextPage(totalWidth);
		initCurrentPage(totalWidth);
	}

	private void initLayoutInflater() {
		LayoutInflater lInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		lInflater.inflate(R.layout.pagingcomponent, this);
	}

	private int getTotalWidth() {
		int totalWidth = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
		return totalWidth;
	}


	private void initCurrentPage(int totalWidth) {
		int currentWidth = (int) (totalWidth * RATIO_CURRENTWIDTH);
		tv_currentpage = (TextView) findViewById(R.id.currentPage);
		tv_currentpage.setText(R.string.currentPage);
		tv_currentpage.setWidth(currentWidth);
	}


	private void  initPreviousAndNextPage(int totalWidth) {
		int nextWidth = (int) (totalWidth * RATIO_NEXTWIDTH);
		tv_previouspage = (TextView) findViewById(R.id.previousPage);
		tv_previouspage.setText(R.string.previousPage);
		tv_previouspage.setWidth(nextWidth);
		
		tv_nextpage = (TextView) findViewById(R.id.nextPage);
		tv_nextpage.setText(R.string.nextPage);
		tv_nextpage.setWidth(nextWidth);
	}

	private void initFirstAndLastPage(int totalWidth) {
		int firstWidth = (int) (totalWidth * RATIO_FIRSTWIDTH);
		tv_firstpage = (TextView) findViewById(R.id.firstPage);
		tv_firstpage.setText(R.string.firstPage);
		tv_firstpage.setWidth(firstWidth);
		
		tv_lastpage = (TextView) findViewById(R.id.lastPage);
		tv_lastpage.setText(R.string.lastPage);
		tv_lastpage.setWidth(firstWidth);
	}

}

2.在需要使用这个控件的activity中引用它

<com.fp.app.pager.PagingComponent
         xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pagingComponent"
	 android:layout_width="fill_parent" android:layout_alignParentBottom="true"
	android:layout_height="wrap_content" />

3. 再用一个专业的class封装其业务逻辑

package com.fp.app.pager;

import java.util.List;

import com.fp.app.entry.R;
import com.fp.app.gotopage.GlobalGotoPageDialog;

import android.app.Activity;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

public class FPPageChanger {
	private List contentList;
	private int pageNumber = 1;
	private int maxPage = 1;
	private int maxCount, eachPageCount;

	private OnPageChangedListener onPageChangedListener;
	private Activity activity;

	private TextView tv_firstpage;
	private TextView tv_prepage;
	private TextView tv_nextpage;
	private TextView tv_lastpage;
	private TextView tv_currentpage;
	private FPPageChangedListener pageListener;

	public FPPageChanger(Activity activity, OnPageChangedListener vOnPageChangedListener) {
		this.activity = activity;
		this.onPageChangedListener = vOnPageChangedListener;
		initListener();
		initPageButtons();
	}

	private void initListener() {
		pageListener = new FPPageChangedListener();
	}

	private void initPageButtons() {
		tv_firstpage = (TextView) activity.findViewById(R.id.firstPage);
		tv_firstpage.setOnClickListener(pageListener);

		tv_prepage = (TextView) activity.findViewById(R.id.previousPage);
		tv_prepage.setOnClickListener(pageListener);

		tv_nextpage = (TextView) activity.findViewById(R.id.nextPage);
		tv_nextpage.setOnClickListener(pageListener);

		tv_lastpage = (TextView) activity.findViewById(R.id.lastPage);
		tv_lastpage.setOnClickListener(pageListener);

		tv_currentpage = (TextView) activity.findViewById(R.id.currentPage);
		tv_currentpage.setOnClickListener(pageListener);
	}

	protected void initButtonText() {
		tv_firstpage.setText(R.string.firstPage);
		tv_currentpage.setText("" + pageNumber + "/" + maxPage);
		tv_lastpage.setText(R.string.lastPage);
	}

	public void setButtonsVisible(boolean visible) {
		LinearLayout buttons = (LinearLayout) activity.findViewById(R.id.pagingComponent);
		if (visible) {
			buttons.setVisibility(View.VISIBLE);
		} else {
			buttons.setVisibility(View.INVISIBLE);
		}
	}

	public boolean isButtonVisible() {
		LinearLayout buttons = (LinearLayout) activity.findViewById(R.id.pagingComponent);
		return buttons.isShown();
	}

	public int getMaxCount() {
		return maxCount;
	}

	public void setMaxCount(int maxCount) {
		this.maxCount = maxCount;
	}

	public int getEachPageCount() {
		return eachPageCount;
	}

	public void setEachPageCount(int eachPageCount) {
		this.eachPageCount = eachPageCount;
	}

	public void setPageEnableAndColor(int pageNum, int totalPage) {
		if (totalPage == 1) {
			setAllPageColor(R.color.solid_grey);
			setAllPageEnable(false);
		} else if (pageNum == 1) {
			setPrePageColor(R.color.solid_grey);
			setPrePageEnable(false);
			setCurrentPageColor(Color.BLACK);
			setCurrentPageEnable(true);
			setNextPageColor(Color.BLACK);
			setNextPageEnable(true);
		} else if (pageNum == totalPage) {
			setNextPageColor(R.color.solid_grey);
			setNextPageEnable(false);
			setCurrentPageColor(Color.BLACK);
			setCurrentPageEnable(true);
			setPrePageColor(Color.BLACK);
			setPrePageEnable(true);
		} else {
			setAllPageColor(Color.BLACK);
			setAllPageEnable(true);
		}
	}

	public void setPageEnableAndColor(int currentPage, int maxCount, int eachPageCount) {
		setButtonsVisible(true);
		setPageNumber(currentPage);
		composePageButtons(maxCount, eachPageCount);
		setPageEnableAndColor(currentPage, this.getMaxPage());
	}

	public void setPrePageEnable(boolean enable) {
		if (enable) {
			tv_firstpage.setEnabled(true);
			tv_prepage.setEnabled(true);
		} else {
			tv_firstpage.setEnabled(false);
			tv_prepage.setEnabled(false);
		}
	}

	public void setPrePageColor(int color) {
		tv_firstpage.setTextColor(color);
		tv_prepage.setTextColor(color);
	}

	public void setNextPageColor(int color) {
		tv_nextpage.setTextColor(color);
		tv_lastpage.setTextColor(color);
	}

	public void setCurrentPageEnable(boolean enable) {
		if (enable) {
			tv_currentpage.setEnabled(true);
		} else {
			tv_currentpage.setEnabled(false);
		}
	}

	public void setCurrentPageColor(int color) {
		tv_currentpage.setTextColor(color);
	}

	public void setNextPageEnable(boolean enable) {
		if (enable) {
			tv_lastpage.setEnabled(true);
			tv_nextpage.setEnabled(true);
		} else {
			tv_lastpage.setEnabled(false);
			tv_nextpage.setEnabled(false);
		}
	}

	public void setAllPageEnable(boolean enable) {
		if (enable) {
			tv_firstpage.setEnabled(true);
			tv_prepage.setEnabled(true);
			tv_currentpage.setEnabled(true);
			tv_lastpage.setEnabled(true);
			tv_nextpage.setEnabled(true);
		} else {
			tv_firstpage.setEnabled(false);
			tv_prepage.setEnabled(false);
			tv_currentpage.setEnabled(false);
			tv_lastpage.setEnabled(false);
			tv_nextpage.setEnabled(false);
		}
	}

	public void setAllPageColor(int color) {
		tv_firstpage.setTextColor(color);
		tv_lastpage.setTextColor(color);
		tv_currentpage.setTextColor(color);
		tv_nextpage.setTextColor(color);
		tv_prepage.setTextColor(color);
	}

	public void setTextViewEnable(boolean enable) {
		if (enable) {
			tv_firstpage.setEnabled(true);
			tv_lastpage.setEnabled(true);
			tv_nextpage.setEnabled(true);
			tv_prepage.setEnabled(true);
		} else {
			tv_firstpage.setEnabled(false);
			tv_lastpage.setEnabled(false);
			tv_nextpage.setEnabled(false);
			tv_prepage.setEnabled(false);
		}
	}

	public void setTextColor(int color) {
		tv_firstpage.setTextColor(color);
		tv_lastpage.setTextColor(color);
		tv_nextpage.setTextColor(color);
		tv_prepage.setTextColor(color);
	}

	
	public void composePageButtons(int maxCount, int eachPageCount) {
		this.eachPageCount = eachPageCount;
		this.maxCount = maxCount;
		calcMaxPage(maxCount);
		initButtonText();
	}


	private void calcMaxPage(int maxCount) {
		if (maxCount % eachPageCount == 0) {
			maxPage = maxCount / eachPageCount;
			if (maxPage == 0)
				maxPage = 1;
		} else {
			maxPage = (int) (maxCount / eachPageCount) + 1;
		}
	}

	
	public void clearView() {
		tv_firstpage.setText(R.string.firstPage);
		tv_currentpage.setText(R.string.currentPage);
		tv_lastpage.setText(R.string.lastPage);
	}

	public void clearData() {
		if (contentList != null && contentList.size() > 0) {
			contentList.clear();
		}
		pageNumber = 1;
		maxPage = 1;
		maxCount = 0;
	}

	public void clearALL() {
		this.clearData();
		this.clearView();
	}

	public int getMaxPage() {
		return maxPage;
	}

	public int getPageNumber() {
		return pageNumber;
	}

	public void setPageNumber(int pageNumber) {
		this.pageNumber = pageNumber;
		this.tv_currentpage.setText("" + pageNumber + "/" + maxPage);
	}

	public void resetMaxPageNumber() {
		tv_lastpage.setText(R.string.lastPage);
	}

	public void resetFirstPageNumber() {
		tv_firstpage.setText(R.string.firstPage);
	}
	

	public interface OnPageChangedListener {
		void onPageDown();

		void onPageUp();

		void onFirstPage();

		void onLastPage();

		void onGotoPage(int pageNum);

	}

	class FPPageChangedListener implements View.OnClickListener {
		public void onClick(View clickedView) {
			try {
				if (clickedView.getId() == R.id.firstPage) {
					if (getPageNumber() > 1) {
						onPageChangedListener.onFirstPage();
					}
				} else if (clickedView.getId() == R.id.previousPage) {
					if (getPageNumber() > 1) {
						onPageChangedListener.onPageUp();
					}
				} else if (clickedView.getId() == R.id.nextPage) {
					int maxPage = getMaxPage();
					int pageIndex = getPageNumber();
					if (pageIndex < maxPage) {
						onPageChangedListener.onPageDown();
					}
				} else if (clickedView.getId() == R.id.lastPage) {
					int maxPage = getMaxPage();
					int pageIndex = getPageNumber();
					if (pageIndex < maxPage) {
						onPageChangedListener.onLastPage();
					}
				} else if (clickedView.getId() == R.id.currentPage) {
					int maxPage = getMaxPage();
					if (maxPage == 1)
						return;

					Handler numHandler = new Handler() {
						public void handleMessage(Message msg) {
							int num = msg.what;
							onPageChangedListener.onGotoPage(num);
						}
					};

					GlobalGotoPageDialog gotoPage = new GlobalGotoPageDialog(activity, numHandler, maxPage);
					gotoPage.show();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}
}

4. 因为每个页面翻页的数据不一样,内部定义了一个接口 OnPageChangedListener ,每个activity实现这个接口即可

protected void initPageChanger() {
		pageChanger = new FPPageChanger(this, this);
	}
FPTocActivity extends Activity implements OnPageChangedListener
	public void onPageDown() {
		if (pageNum == pageChanger.getMaxPage()) {
			return;
		} else {
			pageNum++;
			getDataItem(pageNum, eachPageCount);
		}
	}

	public void onPageUp() {
		if (pageNum == FIRSTPAGE) {
			return;
		} else {
			pageNum--;
			getDataItem(pageNum, eachPageCount);
		}
	}

	public void onFirstPage() {
		pageNum = FIRSTPAGE;
		getDataItem(pageNum, eachPageCount);
	}

	public void onLastPage() {
		pageNum = pageChanger.getMaxPage();
		getDataItem(pageNum, eachPageCount);
	}

	public void onGotoPage(int gotoNum) {
		int currentPageNum = FIRSTPAGE;
		int maxPage = pageChanger.getMaxPage();
		if (gotoNum == pageChanger.getPageNumber() || (gotoNum > maxPage && maxPage == pageChanger.getPageNumber()))
			return;
		if (gotoNum >= maxPage) {
			currentPageNum = maxPage;
		} else
			currentPageNum = gotoNum;
		pageNum = currentPageNum;
		getDataItem(pageNum, eachPageCount);
	}

其他控件实现方法类似,收工!


posted on 2011-07-12 18:14  布兜兜  阅读(7395)  评论(0)    收藏  举报