Android自定义控件
1.组合控件。
几个现有控件,组合起来,达到使用的目的。初始化时,新布局填充到控件上。
组合控件定义一个密码输入框,右侧可以点击图标,来切换密码是显示或隐藏。
布局文件:由editText和img组成。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="60dp"
tools:context="com.example.yijian.testanination.MainActivity">
<EditText
android:id="@+id/et_password"
android:hint="请输入密码"
android:singleLine="true"
android:maxLength="50"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"/>
<RelativeLayout
android:id="@+id/gy_rl_edittext_right"
android:layout_width="48dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:gravity="center"
>
<ImageView
android:id="@+id/gy_img_edittext_right"
android:background="@drawable/gy_password_eye_off"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</RelativeLayout>
定义控件类继承RelativeLayout
public class MyTextView extends RelativeLayout { private RelativeLayout layout = null; private EditText mEtPassword; private RelativeLayout mLlRightPic; public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public MyTextView(Context context) { super(context); init(context); } /** * 初始化 * @param context */ private void init(Context context) { if (layout == null) layout = (RelativeLayout) ((LayoutInflater) (context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))). inflate(R.layout.my_textview,this); mEtPassword = (EditText) layout.findViewById(R.id.et_password); mLlRightPic = (RelativeLayout) layout.findViewById(R.id.gy_rl_edittext_right); mLlRightPic.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //右侧图标点击时,切换 显示密码和隐藏密码的设置 } }); } public String getText(){ //返回用户输入的密码 return mEtPassword.getEditableText().toString(); } }
代码中使用:当做正常控件来使用。
<com.example.yijian.testanination.MyTextView android:layout_width="match_parent" android:layout_height="60dp"> </com.example.yijian.testanination.MyTextView>
2.继承现有控件,实现所需功能。
有些情况下直接继承现有控件,也十分方便。
这种情况要求,对要继承的类较为熟悉。
比如防止Button控件连续点击,可以重写button
/** * Created by gj on 2016/5/11. * 统一按钮 */ public class GyButton extends Button { public GyButton(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public GyButton(Context context) { super(context); init(context); } //button_selecter private void init(Context context) { setBackgroundDrawable(getResources().getDrawable(R.drawable.button)); setTextSize(16); setTextColor(context.getResources().getColor(R.color.textcolor_on_up_widget)); } @Override public boolean performClick() { if(DataTimeUtil.buttonIsDoubleClick()){ return false; }else{ return super.performClick(); } } }
ViewPager添加小圆点指示器
onDraw中去绘制圆点。getAdapter能拿到适配器。getCount可以拿到元素的数量。getCurrentItem当前索引。
这样就可以自动的设置指示器。
/** * Created by gj on 2017/2/8. */ public class IndicatorViewPager extends ViewPager{ Context context; Paint paint; List<Map<String, String>> urls; public IndicatorViewPager(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; paint = new Paint(); } public void setData(List<Map<String, String>> data) { urls.clear(); for (int i = 0; i < data.size(); i++) { urls.add(data.get(i)); } if(urls.size()>0){ this.setVisibility(View.VISIBLE); }else{ this.setVisibility(View.GONE); } } @Override public void draw(Canvas canvas) { super.draw(canvas); drawCycle(canvas); } private void drawCycle(Canvas canvas) { canvas.save(); canvas.translate(getScrollX(), getScrollY()); int count = 0; if (this.getAdapter() != null) { count = this.getAdapter().getCount(); } int select = getCurrentItem(); float density = getContext().getResources().getDisplayMetrics().density; int itemWidth = (int) (11 * density); int itemHeight = itemWidth / 2; int x = (getWidth() - count * itemWidth)/2; int y = getHeight() - itemWidth; int minItemHeight = (int) ((float) itemHeight * 0.8F); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); for (int i = 0; i < count; i++) { if (select == i) { paint.setColor(0xFF666666); canvas.drawCircle((x + itemWidth * i + itemWidth / 2)+350, y, minItemHeight, paint); } else { paint.setColor(0xFFe6e6e6); canvas.drawCircle((x + itemWidth * i + itemWidth / 2)+350, y, minItemHeight, paint); } } canvas.restore(); } }
实现文字的垂直滚动效果。
继承TextSwitcher(文字转换器)。即可正常设置文字。
/** * 自动垂直滚动的TextView */ public class VerticalScrollTextView extends TextSwitcher implements ViewSwitcher.ViewFactory { private Context mContext; //mInUp,mOutUp分别构成向下翻页的进出动画 private Animation mInAnimation; private Animation mOutAnimation; public VerticalScrollTextView(Context context) { this(context, null); } public VerticalScrollTextView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(); } private void init() { //设置TextView的产生,会调用ViewFactory.makeView() setFactory(this); //设置出入动画 mInAnimation = AnimationUtils.loadAnimation(mContext, R.anim.vertical_in); mOutAnimation = AnimationUtils.loadAnimation(mContext, R.anim.vertical_out); setInAnimation(mInAnimation); setOutAnimation(mOutAnimation); } //这里返回的TextView,就是我们看到的View,可以设置自己想要的效果 @Override public View makeView() { TextView textView = new TextView(mContext); textView.setTextSize(36); // textView.setSingleLine(true); // textView.setGravity(Gravity.CENTER_VERTICAL); return textView; } }
动画vertical_in
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="1000" android:fromYDelta="100%p" android:toYDelta="0%p" /> </set>
verrical_out
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="1000" android:fromYDelta="0%p" android:toYDelta="-100%p" /> </set>
3.继承View或者ViewGroup
3.1继承View
Values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="text"format="string" />
<attr name="textSize"format="dimension" />
<declare-styleable name="CustomTxtView">
<attr name="text"/>
<attr name="textSize"/>
</declare-styleable>
</resources>
CustomerView继承成View类
构造方法走三个参数的。
public CustomerView(Context context) { this(context,null); } public CustomerView(Context context, AttributeSet attrs) { this(context, attrs,0); }
public CustomerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); /** * 获得我们所定义的自定义样式属性 */ TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTxtView, defStyleAttr, 0); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.CustomTxtView_text: this.text = a.getString(attr); break; case R.styleable.CustomTxtView_textSize: // 默认设置为16sp,TypeValue也可以把sp转化为px mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics())); break; } } mPaint = new Paint(); mPaint.setTextSize(mTitleTextSize); mBound = new Rect(); mPaint.getTextBounds(text, 0, text.length(), mBound); }
实现onDraw、onMeasure
@Override protected void onDraw(Canvas canvas) { mPaint.setColor(Color.YELLOW); canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); mPaint.setColor(Color.BLACK); canvas.drawText(text, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint); } // 先了解MeasureSpec的specMode,一共三种类型: // EXACTLY:一般是设置了明确的值或者是MATCH_PARENT // AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT // UNSPECIFIED:表示子布局想要多大就多大,很少使用 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height ; if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(text, 0, text.length(), mBound); float textWidth = mBound.width(); int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight()); width = desired; } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(text, 0, text.length(), mBound); float textHeight = mBound.height(); int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom()); height = desired; } setMeasuredDimension(width, height); }
在布局中使用(需要添加约束)
xmlns:customtext="http://schemas.android.com/apk/res-auto"
<com.example.yijian.testanination.widget.CustomerView android:layout_width="wrap_content" android:layout_height="wrap_content" customtext:text="12378" customtext:textSize="16sp" />