作者:chaohx
連結:https://www.jianshu.com/p/9fa393aa1406
問題
程式員最討厭的事情是什麼? 不是程式碼多到寫不完,而是重覆的程式碼要寫無數遍。身為程式員的我們,肯定經常被這個問題所困擾。就比如Android APP中,“我的”頁面中的每一個Item,就像下圖這樣的這樣的,
還有像這種資料錄入頁面的:
像這種頁面佈局是極其常見的,幾乎在每個APP中都有那麼一兩個這樣的頁面。這種頁面讓Android開發人員很是頭疼,開發難度倒是不難,就是重覆程式碼太多,沒有挑戰性。用一句不好聽的話來說就是“程式碼又臭又長”。所以呢,我就對此做了一個封裝,達到一行程式碼就是一行item。
使用效果
先來看一下使用效果,看看封裝後,使用起來有多簡單。
上面的程式碼對有一定Android開發經驗的你應該不難理解,起始就是new一個Item物件,設定其屬性(具體下麵解釋),設定監聽事件(具體下麵解釋),新增到LinnearLayout容器中。
封裝步驟
1、佈局
佈局不難,也就是把通常寫的item佈局抽取出來。主要包括這麼幾個部分:上分割線、下分割線、左Icon、中間文字(偏左那個)、中間輸入框、右邊文字、右邊Icon(預設右箭頭)。
直接上程式碼,根據程式碼裡的註釋,很好理解。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:id="@+id/divider_top"
android:layout_width="fill_parent"
android:layout_height="1px"
android:background="#efefef" />
<LinearLayout
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingTop="10dp">
<ImageView
android:id="@+id/iv_left_icon"
android:layout_width="20dp"
android:layout_height="20dp" />
<TextView
android:id="@+id/tv_text_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_weight="1"
android:textSize="14sp" />
<EditText
android:id="@+id/edit_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:background="@color/transparent"
android:lines="1"
android:maxLines="1"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp" />
<ImageView
android:id="@+id/iv_right_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:src="@mipmap/homepage_right_arrow" />
LinearLayout>
<View
android:id="@+id/divider_bottom"
android:layout_width="fill_parent"
android:layout_height="1px"
android:background="#efefef" />
LinearLayout>
2、MyOneLineView.class的封裝
-
佈局的引入
Xml佈局寫好之後我們還需要一個Java類,取名為MyOneLineView.class
該類繼承LinearLayout或RelativeLayout,使其成為一個容器。
然後,透過LayoutInflater將我們前面寫好的佈局進入MyOneLineView容器中。
public class MyOneLineView extends LinearLayout {
//各各控制元件
private View dividerTop, dividerBottom;
private LinearLayout llRoot;
private ImageView ivLeftIcon;
private TextView tvTextContent;
private EditText editContent;
private TextView tvRightText;
private ImageView ivRightIcon;
public MyOneLineView(Context context) { //該建構式使其,可以在Java程式碼中建立
super(context);
}
public MyOneLineView(Context context, AttributeSet attrs) {//該建構式使其可以在XML佈局中使用
super(context, attrs);
}
/**
* 初始化各個控制元件
*/
public MyOneLineView init() {
//引入之前的xml佈局
LayoutInflater.from(getContext()).inflate(R.layout.layout_my_one_line, this, true);
llRoot = (LinearLayout) findViewById(R.id.ll_root);
dividerTop = findViewById(R.id.divider_top);
dividerBottom = findViewById(R.id.divider_bottom);
ivLeftIcon = (ImageView) findViewById(R.id.iv_left_icon);
tvTextContent = (TextView) findViewById(R.id.tv_text_content);
editContent = (EditText) findViewById(R.id.edit_content);
tvRightText = (TextView) findViewById(R.id.tv_right_text);
ivRightIcon = (ImageView) findViewById(R.id.iv_right_icon);
return this;
}
}
這樣 MyOneLineView.class 每 new 一下就代表這一個 Item。只不過他的樣式還只是預設狀態。下麵我們就對它的樣式操作進行封裝。
-
分割線封裝
前面佈局中也介紹了,每個Item都有上下兩條分割線,預設上分割線隱藏,下分割線顯示,寬度充滿全屏,高度1dp,顏色是灰色。
我們封裝幾個方法用於對分割線樣式的改變:
/**
* 設定上下分割線的顯示情況
*
* @return
*/
public MyOneLineView showDivider(Boolean showDividerTop, Boolean showDivderBottom) {
if (showDividerTop) {
dividerTop.setVisibility(VISIBLE);
} else {
dividerTop.setVisibility(GONE);
}
if (showDivderBottom) {
dividerBottom.setVisibility(VISIBLE);
} else {
dividerBottom.setVisibility(GONE);
}
return this;
}
/**
* 設定上分割線的顏色
*
* @return
*/
public MyOneLineView setDividerTopColor(int dividerTopColorRes) {
dividerTop.setBackgroundColor(getResources().getColor(dividerTopColorRes));
return this;
}
/**
* 設定上分割線的高度
*
* @return
*/
public MyOneLineView setDividerTopHigiht(int dividerTopHigihtDp) {
ViewGroup.LayoutParams layoutParams = dividerTop.getLayoutParams();
layoutParams.height = DensityUtils.dp2px(getContext(), dividerTopHigihtDp);
dividerTop.setLayoutParams(layoutParams);
return this;
}
-
每個child的封裝
同上面對分割線樣式的封裝,我們就可以對每個child的樣式提供方法,需要怎樣改變child的樣式就封裝對應的方法即可。直接看程式碼:
/**
* 設定root的paddingTop 與 PaddingBottom 從而控制整體的行高
* paddingLeft 與 paddingRight 保持預設 20dp
*/
public MyOneLineView setRootPaddingTopBottom(int paddintTop, int paddintBottom) {
llRoot.setPadding(DensityUtils.dp2px(getContext(), 20),
DensityUtils.dp2px(getContext(), paddintTop),
DensityUtils.dp2px(getContext(), 20),
DensityUtils.dp2px(getContext(), paddintBottom));
return this;
}
/**
* 設定root的paddingLeft 與 PaddingRight 從而控制整體的行高
*
* paddingTop 與 paddingBottom 保持預設 15dp
*/
public MyOneLineView setRootPaddingLeftRight(int paddintTop, int paddintRight) {
llRoot.setPadding(DensityUtils.dp2px(getContext(), paddintTop),
DensityUtils.dp2px(getContext(), 15),
DensityUtils.dp2px(getContext(), paddintRight),
DensityUtils.dp2px(getContext(), 15));
return this;
}
/**
* 設定左邊Icon
*
* @param iconRes
*/
public MyOneLineView setLeftIcon(int iconRes) {
ivLeftIcon.setImageResource(iconRes);
return this;
}
/**
* 設定左邊Icon顯示與否
*
* @param showLeftIcon
*/
public MyOneLineView showLeftIcon(boolean showLeftIcon) {
if (showLeftIcon) {
ivLeftIcon.setVisibility(VISIBLE);
} else {
ivLeftIcon.setVisibility(GONE);
}
return this;
}
/**
* 設定右邊Icon 以及Icon的寬高
*/
public MyOneLineView setLeftIconSize(int widthDp, int heightDp) {
ViewGroup.LayoutParams layoutParams = ivLeftIcon.getLayoutParams();
layoutParams.width = DensityUtils.dp2px(getContext(), widthDp);
layoutParams.height = DensityUtils.dp2px(getContext(), heightDp);
ivLeftIcon.setLayoutParams(layoutParams);
return this;
}
/**
* 設定中間的文字內容
*
* @param textContent
* @return
*/
public MyOneLineView setTextContent(String textContent) {
tvTextContent.setText(textContent);
return this;
}
/**
* 設定中間的文字顏色
*
* @return
*/
public MyOneLineView setTextContentColor(int colorRes) {
tvTextContent.setTextColor(getResources().getColor(colorRes));
return this;
}
/**
* 設定中間的文字大小
*
* @return
*/
public MyOneLineView setTextContentSize(int textSizeSp) {
tvTextContent.setTextSize(textSizeSp);
return this;
}
這一部分程式碼都比較類似,所以就貼出部分程式碼來。
細心的同學會發現,大部分的方法都是傳回了this.這樣做的原因就是想實現鏈式結構,鏈式結構也是最近比較流行的。
程式碼中有這樣的程式碼:DensityUtils.dp2px(getContext(), widthDp);
DensityUtils 也是一個封裝的工具類,用於dp sp 與px 的轉換,用興趣的同學可以看這篇文章Android單位轉換—-常用單位轉換工具類
-
點選事件的封裝
樣式有了,接下來就應該考慮點選事件了,這一類item通常有兩個點選事件,一整行的點選事件、右箭頭的點選事件。因此這裡封裝了兩個ClickListener
public class MyOneLineView extends LinearLayout {
....
/**
* 整個一行被點選
*/
public static interface OnRootClickListener {
void onRootClick(View view);
}
/**
* 右邊箭頭的點選事件
*/
public static interface OnArrowClickListener {
void onArrowClick(View view);
}
public MyOneLineView setOnRootClickListener(final OnRootClickListener onRootClickListener, final int tag) {
llRoot.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
llRoot.setTag(tag);
onRootClickListener.onRootClick(llRoot);
}
});
return this;
}
public MyOneLineView setOnArrowClickListener(final OnArrowClickListener onArrowClickListener, final int tag) {
ivRightIcon.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ivRightIcon.setTag(tag);
onArrowClickListener.onArrowClick(ivRightIcon);
}
});
return this;
}
....
}
小夥伴們可能會問,這裡為什麼要傳入tag。不用急,請看下麵的使用程式碼:
public class MineFragment extends SimpleFragment implements MyOneLineView.OnRootClickListener{
......
initView(){
//新增我的下載
llRoot.addView(new MyOneLineView(getContext())
.initMine(R.mipmap.mine_download, "我的下載", "", true)
.setOnRootClickListener(this, 1));
//新增我的收藏
llRoot.addView(new MyOneLineView(getContext())
.initMine(R.mipmap.mine_collection, "我的收藏", "", true)
.setOnRootClickListener(this, 2));
}
@Override
public void onRootClick(View view) {
switch ((int) view.getTag()) {
case 1:
startActivity(new Intent(getContext(), MyDownloadActivity.class));
break;
case 2:
startActivity(new Intent(getContext(), MyCollectionActivity.class));
break;
}
......
}
看了這段程式碼,大家應該直到為什麼要設定tag了吧。原因就是希望共用onRootClick方法。有同學會問,為什麼不用id呢?那是因為,每一個item用的是同一個xml佈局,也就是用的同一個id,所以這裡不能像以往一樣用id。
-
常用場景init()
到這裡MyOnLineView已經封裝好了,但是為了方便,我們可以為使用頻率很高的一些情況提供快速的建立方法,就想“我的”頁面的item。大家可以更具自己專案不同來做調整。
/**
* 預設情況下的樣子 icon + 文字 + 右箭頭 + 下分割線
*
* @param iconRes icon圖片
* @param textContent 文字內容
*/
public MyOneLineView init(int iconRes, String textContent) {
init();
showDivider(false, true);
setLeftIcon(iconRes);
setTextContent(textContent);
showEdit(false);
setRightText("");
showArrow(true);
return this;
}
/**
* 我的頁面每一行 icon + 文字 + 右箭頭(顯示/不顯示) + 右箭頭左邊的文字(顯示/不顯示)+ 下分割線
*
* @param iconRes icon圖片
* @param textContent 文字內容
*/
public MyOneLineView initMine(int iconRes, String textContent, String textRight, boolean showArrow) {
init(iconRes, textContent);
setRightText(textRight);
showArrow(showArrow);
return this;
}
/**
* icon + 文字 + edit + 下分割線
*
* @return
*/
public MyOneLineView initItemWidthEdit(int iconRes, String textContent, String editHint) {
init(iconRes, textContent);
showEdit(true);
setEditHint(editHint);
showArrow(false);
return this;
}
總結
文章到此結束,希望這篇文章對大家有所幫助,提高開發效率。
完整程式碼:https://github.com/chaohengxing/MyOneLineView.git
●編號364,輸入編號直達本文
●輸入m獲取到文章目錄
Java程式設計
更多推薦《18個技術類公眾微信》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。