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);
}
其他控件实现方法类似,收工!
浙公网安备 33010602011771号