包裹子视图
TextView
、开关Switch
、单选RadioButton
、图片ImageView
、多选CheckBox
、按钮Button
、输入EditText
包裹子视图
ViewParent
和ViewManager
接口布局类
(AbsoluteLayout已过时,官方不推荐使用)layout | remark |
---|---|
LinearLayout |
在单列中水平排列或在单行中垂直排列其他视图的布局 |
TableLayout |
将子级排列成行和列的布局 |
FrameLayout |
在屏幕上屏蔽一个区域以显示单个项目 |
RelativeLayout |
在这种布局中,子对象的位置可以相互描述或与父对象描述。 |
GridLayout |
将其子项放置在矩形网格中的布局 |
AbsoluteLayout |
允许您指定其子级的精确位置(X/Y坐标)的布局 |
Category | Method | Description |
---|---|---|
创建 | Constructors | 应用layout布局文件中的属性 |
onFinishInflate() | 在视图及其所有子视图都已从XML注入后调用 | |
布局 | onMeasure() | 调用以确定此视图及其所有子视图的大小要求 |
onLayout() | 当此视图应为其所有子视图指定大小和位置时调用 | |
onSizeChanged() | 当此视图的大小改变时调用 | |
画图 | onDraw() | 在视图应呈现其内容时调用 |
事件处理 | onKeyDown() | 在发生硬件事件时调用 |
onKeyUp() | 当发生硬件启动事件时调用 | |
onTrackballEvent() | 当轨迹球运动事件发生时调用 | |
onTouchEvent() | 当触摸屏运动事件发生时调用 | |
焦点 | onFocusChanged() | 当视图获得或失去焦点时调用 |
onWindowFocusChanged() | 当包含视图的窗口获得或失去焦点时调用 | |
依附 | onAttachedToWindow() | 当视图附在到窗口时调用 |
onDetachedFromWindow() | 当视图与其窗口分离时调用 | |
onWindowVisibilityChanged() | 当包含视图的窗口的可见性发生改变时调用 |
本文主要及介绍的是自定义View
(继承android.view.View
),一个小时分钟选择器TimerChooser
,地址:https://github.com/15045120/iAlarmClock,效果图如下:
<resources>
<declare-styleable name="TimerChooser">
<attr name="hour" format="integer"/>
<attr name="minute" format="integer"/>
<attr name="textColor" format="color"/>
<attr name="highlightTextColor" format="color"/>
<attr name="highlightLineColor" format="color"/>
declare-styleable>
resources>
添加完attr.xml
并做完3.2步
后在layout
文件中加入如下代码即可使用以下自定义属性
xmlns:app="http://schemas.android.com/apk/res-auto"
property | remark |
---|---|
app:hour | 小时的值 |
app:minute | 分钟的值 |
app:textColor | 未被选中字体颜色 |
app:highlightTextColor | 被选中字体颜色 |
app:highlightLineColor | 被选中线颜色 |
主要看第4个
构造方法,将我们在xml中自定义的属性
值设置好,如app:textColor
设置未被选中字体颜色,可通过mTextColor = a.getColor(attr, Color.GRAY);
获取xml布局文件中TimerChooser控件的app:textColor
属性值
public TimerChooser(Context context) {
this(context,null);
}
public TimerChooser(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimerChooser(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public TimerChooser(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// in a condition when a.getIndexCount() is equals to zero (TypedArray a)
mHour = NOW_TIME.get(Calendar.HOUR_OF_DAY);
mMinute = NOW_TIME.get(Calendar.MINUTE);
mBlock = NO_BLOCK;
mTextColor = Color.GRAY;
mHighlightTextColor = Color.RED;
mHighlightLineColor = Color.RED;
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.TimerChooser, defStyleAttr, defStyleRes);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.TimerChooser_hour:
mHour = a.getInt(attr, NOW_TIME.get(Calendar.HOUR_OF_DAY));
break;
case R.styleable.TimerChooser_minute:
mMinute = a.getInt(attr, NOW_TIME.get(Calendar.MINUTE));
break;
case R.styleable.TimerChooser_textColor:
mTextColor = a.getColor(attr, Color.GRAY);
break;
case R.styleable.TimerChooser_highlightTextColor:
mHighlightTextColor = a.getColor(attr, Color.RED);
break;
case R.styleable.TimerChooser_highlightLineColor:
mHighlightLineColor = a.getColor(attr, Color.RED);
break;
default:
break;
}
}
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, getDefaultHeight(heightMeasureSpec));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
doOnDraw(canvas);
}
/**
* Interface definition for a callback to be invoked
* when TimerChooser value changed.
*/
public interface OnTimerValueListener {
/**
* When Timer Value changed, notify changed value.
*
* @param v The view the key has been dispatched to.
* @param hour The hour value users have selected.
* @param minute The minute value users have selected.
*/
void onTimerValueChanged(View v, int hour, int minute);
}
MotionEvent.ACTION_DOWN
):记录下手指操作的当前区域,小时Block或者分钟BlockMotionEvent.ACTION_MOVE
):不作处理MotionEvent.ACTION_UP
):计算出滑动后的小时和分钟数值,并使用mOnTimerValueListener
通知小时分钟值的变化@Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
Log.d(TAG, eventX+"/"+eventY);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"ACTION_DOWN");
if(eventX > mTimerDrawable.mStartX&&
eventX < mTimerDrawable.mStartX + mTimerDrawable.mViewHalfWidth){
mBlock = HOUR_BLOCK;
Log.d(TAG,"HOUR_BLOCK");
}else if(eventX > mTimerDrawable.mCenterX&&
eventX < mTimerDrawable.mCenterX + mTimerDrawable.mViewHalfWidth){
mBlock = MINUTE_BLOCK;
Log.d(TAG,"MINUTE_BLOCK");
}else{
mBlock = NO_BLOCK;
Log.d(TAG,"NO_BLOCK");
}
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"ACTION_UP");
if(eventY < mTimerDrawable.mStartY){
if(mBlock == HOUR_BLOCK){
mHour = (mHour+24+1) %24;
}else if(mBlock == MINUTE_BLOCK){
mMinute = (mMinute+60+1) %60;
}else{
}
}else if(eventY > mTimerDrawable.mStartY + mTimerDrawable.mViewHeight){
if(mBlock == HOUR_BLOCK){
mHour = (mHour+24) %24 == 0?23:(mHour+24) %24-1;
}else if(mBlock == MINUTE_BLOCK){
mMinute = (mMinute+60) %60 == 0?59:(mMinute+60) %60-1;
}else{
}
}
// call the listener method
if(mOnTimerValueListener != null){
mOnTimerValueListener.onTimerValueChanged(this, mHour, mMinute);
}
invalidate();
break;
default:
break;
}
return true;
}
<org.ibu.ialarmclock.view.TimerChooser
android:id="@+id/timer_chooser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:textColor="#000000"
/>
TimerChooser timerChooser = findViewById(R.id.timer_chooser);
timerChooser.setTimerValueListener(new TimerChooser.OnTimerValueListener() {
@Override
public void onTimerValueChanged(View v, int hour, int minute) {
Log.d(TAG, hour+"/"+minute);
}
});
详见官网,这里不赘述了。