最近忙得好久沒寫博客了,今天再次回歸,總結鞏固自己學的東西。在android中都覺得寫控件是一件比較難的事,其實并不難,這篇博客來講講如何自定義控件,并自定了我在項目中常用的一個控件,先看效果圖:

話不多說,直接進入主題。
看看上圖的圓框,我們要先畫出此界面。布局如下:
~~~
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rel"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/global_advsearch_item_shape" >
<TextView
android:id="@+id/text1"
android:layout_width="35dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerInParent="true"
android:layout_marginLeft="11dp"
android:textColor="#444444"
/>
<View
android:id="@+id/view1"
android:layout_width="1dp"
android:layout_height="15dp"
android:layout_marginLeft="6dp"
android:layout_centerInParent="true"
android:layout_toRightOf="@id/text1"
android:background="#CCCCCC" />
<TextView
android:id="@+id/edit1"
android:layout_toRightOf="@id/view1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:background="@null"
android:layout_marginLeft="13dp"
android:layout_marginRight="13dp"
android:gravity="left|center"
android:hint="請選擇"
android:textColor="#444444"
/>
</RelativeLayout>
~~~
外框的背景也要用drawable下的shape來畫,定義好弧度和框的顏色及粗度,如下:
~~~
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#FFFFFF" />
<stroke android:color="#F56A55"
android:width="1dp" />
<corners android:radius="100dp" />
</shape>
~~~
既然是自定義的組件,那么組件的屬性也要可以設置。需要定義以下的屬性文件,在attrs.xml中:
~~~
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="OutButton">
<attr name="btnbackground" format="reference|color"/>
<attr name="leftText" format="string"/>
<attr name="leftSize" format="dimension"/>
<attr name="leftColor" format="color"/>
<attr name="rightColor" format="color"/>
<attr name="rightText" format="string"/>
<attr name="rightSize" format="dimension"/>
</declare-styleable>
</resources>
~~~
以上的屬性是隨意添加的。其中leftText是控制左textView的文字,leftColor是控制文字的顏色。以此類推。format是定義屬性的類型,如string是指屬性要定義成字符串,dimension指的是大小,如12sp之類的。color是顏色。reference可以是引用 ,如設置圖片背景的時候引用drawable下的文件。
然后就是定義我的組件,為了方便讓它繼承自FrameLayout,代碼 如下:
~~~
public class customButton extends FrameLayout {
private TextView leftText;
private TextView rightText;
private RelativeLayout rel;
private OutClickListener mlistener = null;
private int flag;
public customButton(final Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.outbutton, this);
leftText = (TextView)super.findViewById(R.id.text1);
rightText = (TextView)super.findViewById(R.id.edit1);
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.OutButton);
leftText.setText(ta.getString(R.styleable.OutButton_leftText));
rightText.setText(ta.getString(R.styleable.OutButton_rightText));
leftText.setTextSize(ta.getDimension(R.styleable.OutButton_leftSize, 10));
rightText.setTextSize(ta.getDimension(R.styleable.OutButton_rightSize, 10));
//rel.setBackground(ta.getDrawable(R.styleable.OutButton_btnbackground));
rightText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(mlistener !=null)
mlistener.popListener();
/*int a = mlistener.getFlag();
Intent i = new Intent(context,CustomDialogActivity.class);
context.ststartActivityForResult(i,1);*/
}
});
ta.recycle();
}
public void setOutClickListener(OutClickListener listener){
this.mlistener = listener;
}
public interface OutClickListener{
void popListener();
//int getFlag();
}
}
~~~
其實很簡單,逐一講解。
~~~
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.OutButton);
~~~
指的是獲取我們剛剛定義的屬性文件。然后將其中定義的屬性賦給控件,這樣屬性就與控件綁定到一起。為了使控件有點擊事件,我們需要定義 一個接口OutClickListener,并在rightText的點擊事件中調用接口的方法。最后通過setOnClickListener()把接口暴露給調用者,這樣就可以通過回調在外層寫點擊事件。也保證也控件的解藕。
這樣控件就定義完了,來看看如何使用。為了可以使用屬性,要在布局文件的命名空間加入如下一行聲明:
~~~
xmlns:lxj="http://schemas.android.com/apk/res-auto"
~~~
接下來就可以自定義屬性了,如下:
~~~
<com.example.linxj.customoutbutton.customButton
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
lxj:leftText="時間"
lxj:leftSize="8sp"
lxj:rightSize ="8sp"
lxj:rightText="請點擊"></com.example.linxj.customoutbutton.customButton>
~~~
這樣就可以顯示了。接下來我們用這個控件來完成彈出框的功能。在一個項目中,可能會多次很到不同的彈出框,為此可以定義一個Dialog形式的Activity,并實現多個Dialog的復用。先看看代碼:
~~~
public class CustomDialogActivity extends Activity {
private List<String> dataList;
private ListView listView;
private Button cancelButton;
private String[] dataSource;
private int flag;
private static final float RATIO = 5/10f;
private String templeContent = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.setContentView(R.layout.global_popwin_main);
dataList = new ArrayList<String>();
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.width = LayoutParams.FILL_PARENT;
lp.gravity = Gravity.BOTTOM;
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
getWindow().setAttributes(lp);
initViews();
}
private void initViews(){
Bundle bundle = getIntent().getExtras();
flag = bundle.getInt("flag");
initData(flag);
cancelButton = (Button) findViewById(R.id.cancel_but);
listView = (ListView) findViewById(R.id.select);
listView.setAdapter(new SessionBaseAdapter());
listView.setOnItemClickListener(new OnItemClickListenerImpl());
setPopWindowSize(flag);
cancelButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putInt("flag", flag);
bundle.putString("content", "");
intent.putExtras(bundle);
setResult(RESULT_OK, intent);
finish();
}
});
}
private void initData(int flag){
dataList = new ArrayList<String>();
switch (flag) {
case 0:
Bundle bundle = getIntent().getExtras();
dataSource = bundle.getStringArray("project_year");
case 1:
dataSource = getResources().getStringArray(R.array.startYear);
break;
}
for (int i = 0; i < dataSource.length; i++) {
dataList.add(dataSource[i]);
}
}
private void setPopWindowSize(int flag){
switch (flag) {
case 0:
case 1:
float screenHeight = this.getResources().getDisplayMetrics().heightPixels;
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) listView.getLayoutParams();
layoutParams.height = (int) (screenHeight * RATIO);
listView.setLayoutParams(layoutParams);
break;
}
}
final class ViewHolder {
public ImageView image;
public TextView itemName;
public RelativeLayout layout;
}
class SessionBaseAdapter extends BaseAdapter {
public SessionBaseAdapter() {}
@Override
public int getCount() {
return dataList.size();
}
@Override
public Object getItem(int postion) {
return postion;
}
@Override
public long getItemId(int postion) {
return postion;
}
@Override
public View getView(final int postion, View convertView, ViewGroup parent) {
final ViewHolder holder = new ViewHolder();
convertView = LayoutInflater.from(CustomDialogActivity.this).inflate(R.layout.global_popwin_listitem, null);
holder.layout = (RelativeLayout) convertView.findViewById(R.id.type1_layout_1);
holder.image = (ImageView) convertView.findViewById(R.id.icon);
holder.itemName = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
holder.itemName.setText(dataList.get(postion));
holder.layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
holder.itemName.setTextColor(Color.parseColor("#F56A55"));
holder.image.setVisibility(View.VISIBLE);
templeContent = dataList.get(postion);
startIntent();
}
});
return convertView;
}
}
private void startIntent() {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putInt("flag", flag);
bundle.putString("content", templeContent);
intent.putExtras(bundle);
setResult(RESULT_OK, intent);
this.finish();
}
public class OnItemClickListenerImpl implements OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int postion, long id) {
ViewHolder holder = (ViewHolder) view.getTag();
holder.image.setVisibility(View.VISIBLE);
holder.itemName.setTextColor(Color.parseColor("#F56A55"));
templeContent = dataList.get(postion);
startIntent();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(CustomDialogActivity.this, event)) {
startIntent();
return true;
}
return super.onTouchEvent(event);
}
private boolean isOutOfBounds(Activity context, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
final View decorView = context.getWindow().getDecorView();
return (x < -slop) || (y < -slop)|| (x > (decorView.getWidth() + slop))|| (y > (decorView.getHeight() + slop));
}
@Override
public void onBackPressed() {
super.onBackPressed();
startIntent();
}
}
~~~
代碼很長,但總結下來就是,傳不同的flag進去,定義不同的Dialog樣式。上面代碼為了方便只舉了兩個flag,其實在項目中你可以定義很多分類。并定義不同樣式的Dialog。
以上代碼可以傳不同的數組進去,它都能正常的顯示出來,復用性強。接下來我們來看看MainActivity如何調用。
~~~
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cBtn = (customButton)super.findViewById(R.id.btn);
cBtn.setOutClickListener(new customButton.OutClickListener() {
@Override
public void popListener() {
Bundle b = new Bundle();
b.putInt("flag", 1);
Intent i = new Intent(MainActivity.this,CustomDialogActivity.class).putExtras(b);
startActivityForResult(i,1);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
Toast.makeText(MainActivity.this,data.getStringExtra("content"),Toast.LENGTH_SHORT).show();
}
}
~~~
好的,很簡單,傳入一個flag。而出來的年代數據是定義在布局文件string-array中的。
有的控件可能自定義進來很難,但這都是一個迭代的過程,再復雜的控件也是從簡單控件入手的。之后會給出代碼的下載地址。
[源碼下載地址](https://github.com/reallin/CustomOutButton.git)
- 前言
- Android底部tab與標題欄相結合
- Android免費獲取短信驗證碼
- android中Handler的源碼分析
- 詳解Fragment的傳值問題
- 詳談gson解析
- android新控件之toolbar,floatingActionButton,SnackBar,CollapsingToolbarLayout
- android自定義控件
- 淺談android的線程池
- Android的消息處理機制,AsyncTask源碼解析
- IPC——android進程間通信
- CoCos2d_android入門所需知道的一切
- Cocos2d_android你所需要知道的一切(下)
- Activity你需要知道的一切
- Activity啟動過程源碼分析
- Data Binding Guide——google官方文檔翻譯(上)
- Data Binding Guide——google官方文檔翻譯(下)
- android TextView實現跑馬燈效果
- android中生成excel
- Volley源碼解析
- LayoutInflater源碼解析
- android發送郵件
- android測試工具MonkeyRunner--google官網翻譯
- android View繪制源碼分析
- Android中Window添加View的底層原理
- 仿美團商品選購下拉菜單實現