就像其他开发平台一样,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); }
其他控件实现方法类似,收工!