第二章 控件架构与自定义控件详解 + ListView使用技巧 + Scroll分析

1.Android控件架构
下图是UI界面架构图,每个Activity都有一个Window对象,通常是由PhoneWindow类来实现的。
PhoneWindow将DecorView作为整个应用窗口的根View,DecorView将屏幕分成两部分:TitleView和ContentView。
ContentView实际上是一个FrameLayout,里面容纳的就是我们在xml布局文件中定义的布局。

为什么调用requestWindowFeature()方法一定要在setContentView()方法调用之前?
当程序在onCreate()方法中调用setContentView()方法后,ActivityManagerService会回调onResume()方法,此时系统才会将整个DecorView添加到PhoneWindow中,并让其显示出来,从而完成界面的绘制。

2.View的测量:MeasureSpec和测量模式
MeasureSpec是一个32位的int值,其中高2位是测量的模式,低30位是测量的大小 (使用位运算是为了提高效率和节省空间)
测量模式有三种:
(1)EXACTLY:精确值模式,属性设置为精确数值或者match_parent时,系统使用的是EXACTLY模式
(2)AT_MOST:最大值模式,属性设置为wrap_content时,系统使用的是AT_MOST模式
(3)UNSPECIFIED:不指定大小测量模式,通常情况下在绘制自定义View时才会用到

View类默认的onMeasure()方法只支持EXACTLY模式,所以如果在自定义View的时候不重写onMeasure方法的话,就只能使用EXACTLY模式。自定义View可以响应你指定的具体的宽高值或者是match_parent属性,但是,如果要让自定义View支持wrap_content属性的话,那么就必须要重写onMeasure方法来指定wrap_content时view的大小。

重写onMeasure方法的最终工作就是把测量后的宽高值作为参数设置给setMeasuredDimension方法。

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    //计算width和height    setMeasuredDimension(width, height);}

3.View和ViewGroup的绘制
View的onDraw()方法包含一个参数Canvas对象,使用这个Canvas对象就可以进行绘图了。

通常情况下,Canvas对象的创建需要传入参数Bitmap,为什么呢?
这是因为传进去的Bitmap与通过这个Bitmap创建的Canvas画布是紧紧联系在一起的,这个Bitmap用来存储所有绘制在Canvas上的像素信息,当使用Bitmap创建Canvas之后,后面调用所有的Canvas.drawXXX方法都发生在这个Bitmap上。

ViewGroup通常不需要绘制,因为它本身没有需要绘制的东西,如果不指定ViewGroup的背景颜色,那么ViewGroup的onDraw方法都不会被调用。但是,ViewGroup会调用dispatchDraw方法来绘制其子view,其过程同样是通过遍历所有子view并调用子view的绘制方法来完成绘制工作的。

4.自定义View(ViewGroup)
三种自定义View的方式:
(1)对现有控件进行扩展
对现有控件进行扩展的代码结构通常如下:

@Overrideprotected void onDraw(Canvas canvas) {    //在回调父类方法之前实现自己的逻辑,对TextView来说就是在绘制文本之前    super.onDraw(canvas);    //在回调父类方法之后实现自己的逻辑,对TextView来说就是在绘制文本之后}

例如,书中对TextView进行扩展代码节选

private void initView() {    mPaint1 = new Paint();    mPaint1.setColor(getResources().getColor(android.R.color.holo_blue_light));    mPaint1.setStyle(Paint.Style.FILL);    mPaint2 = new Paint();    mPaint2.setColor(Color.YELLOW);    mPaint2.setStyle(Paint.Style.FILL);}

@Overrideprotected void onDraw(Canvas canvas) {    // 绘制外层矩形    canvas.drawRect(            0,            0,            getMeasuredWidth(),            getMeasuredHeight(),            mPaint1);    // 绘制内层矩形    canvas.drawRect(            10,            10,            getMeasuredWidth() - 10,            getMeasuredHeight() - 10,            mPaint2);    canvas.save();    // 绘制文字前平移10像素    canvas.translate(10, 0);    // 父类完成的方法,即绘制文本    super.onDraw(canvas);    canvas.restore();}

(2)通过组合来实现新的控件
这种方式通常需要继承一个合适的ViewGroup,再给它添加指定功能的控件,从而组合成新的复合控件。
[项目中一般使用这种方式创建应用内统一的提示信息界面,可以是提示正在加载,也可以是提示数据出错了等]
例如,书中的TopBar例子:

public class TopBar extends RelativeLayout {

    // 包含topbar上的元素:左按钮、右按钮、标题    private Button mLeftButton, mRightButton;    private TextView mTitleView;

    // 布局属性,用来控制组件元素在ViewGroup中的位置    private LayoutParams mLeftParams, mTitlepParams, mRightParams;

    // 左按钮的属性值,即我们在atts.xml文件中定义的属性    private int mLeftTextColor;    private Drawable mLeftBackground;    private String mLeftText;    // 右按钮的属性值,即我们在atts.xml文件中定义的属性    private int mRightTextColor;    private Drawable mRightBackground;    private String mRightText;    // 标题的属性值,即我们在atts.xml文件中定义的属性    private float mTitleTextSize;    private int mTitleTextColor;    private String mTitle;

    // 映射传入的接口对象    private topbarClickListener mListener;    ......}

(3)重写View来实现全新的控件
创建自定义View的难点在于绘制控件和实现交互,通常需要继承View类,并重写onDraw、onMeasure等方法来实现绘制逻辑,同时通过重写onTouchEvent等触控事件方法来实现交互逻辑。
[这类自定义View是比较常用的,自己以前也写过几个简单的例子,参见AnnotationViewProgressView项目,或者参考之前的博文Android Text View with Custom Font,一个可以自定义字体的TextView]
例如,书中的弧线展示图例子

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    mMeasureWidth = MeasureSpec.getSize(widthMeasureSpec);    mMeasureHeigth = MeasureSpec.getSize(heightMeasureSpec);    setMeasuredDimension(mMeasureWidth, mMeasureHeigth);    initView();}

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    // 绘制圆    canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);    // 绘制弧线    canvas.drawArc(mArcRectF, 270, mSweepAngle, false, mArcPaint);    // 绘制文字    canvas.drawText(mShowText, 0, mShowText.length(), mCircleXY, mCircleXY + (mShowTextSize / 4), mTextPaint);}

5.事件拦截机制分析 [后面有专门对Android事件拦截机制分析的部分,此处略过]

第四章 ListView使用技巧

1.使用ViewHolder模式提高效率
这种方式是必须要用的!下面的例子是一个常见的使用ViewHolder并且包含多个item type的Adapter例子:

public class ChatItemListViewAdapter extends BaseAdapter {

    private List<ChatItemListViewBean> mData;    private LayoutInflater mInflater;

    public ChatItemListViewAdapter(Context context, List<ChatItemListViewBean> data) {        this.mData = data;        mInflater = LayoutInflater.from(context);    }

    @Override    public int getCount() {        return mData.size();    }

    @Override    public Object getItem(int position) {        return mData.get(position);    }

    @Override    public long getItemId(int position) {        return position;    }

    @Override    public int getItemViewType(int position) {        ChatItemListViewBean bean = mData.get(position);        return bean.getType();    }

    @Override    public int getViewTypeCount() {        return 2;    }

    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder holder;        if (convertView == null) {            if (getItemViewType(position) == 0) {                holder = new ViewHolder();                convertView = mInflater.inflate(R.layout.chat_item_itemin, null);                holder.icon = (ImageView) convertView.findViewById(R.id.icon_in);                holder.text = (TextView) convertView.findViewById(R.id.text_in);            } else {                holder = new ViewHolder();                convertView = mInflater.inflate(R.layout.chat_item_itemout, null);                holder.icon = (ImageView) convertView.findViewById(R.id.icon_out);                holder.text = (TextView) convertView.findViewById(R.id.text_out);            }            convertView.setTag(holder);        } else {            holder = (ViewHolder) convertView.getTag();        }        holder.icon.setImageBitmap(mData.get(position).getIcon());        holder.text.setText(mData.get(position).getText());        return convertView;    }

    public final class ViewHolder {        public ImageView icon;        public TextView text;    }

}

2.listview的一些属性设置
(1)设置分隔线
android:divider=""@android:color/white"
android:dividerHeight="10dp"
android:divider="@null" (设置分隔线透明)
(2)隐藏滚动条
android:scrollbars="none"
(3)取消item的点击效果
android:listSelector="@android:color/transparent"

3.listview的一些方法设置
(1)设置listview显示在第几项
listview.setSelection(n); 这个方法类似scrollTo瞬间完成移动,平滑移动可以使用下面的方式
listview.smoothScrollBy(distance, duration);
listview.smoothScrollByOffset(offset);
listview.smoothScrollToPosition(index);
(2)处理空listview
listview.setEmptyView(View)

4.动态修改listview
在使用adapter的notifyDataSetChanged方法时,必须保证传进adapter的数据list和发生数据变化的list是同一个对象,否则将无法看到效果。

5.listview滑动监听
监听listview的滑动事件的方法有两种:一个是OnTouchListener来实现监听,另一个是使用OnScrollListener来实现监听。
例如,书中实现了一个监听listview上下滑动事件操纵toolbar显示和隐藏效果的例子:

public class ScrollHideListView extends Activity {

    private Toolbar mToolbar;    private ListView mListView;    private String[] mStr = new String[20];    private int mTouchSlop;    private float mFirstY;    private float mCurrentY;    private int direction;    private ObjectAnimator mAnimator;    private boolean mShow = true;

    View.OnTouchListener myTouchListener = new View.OnTouchListener() {        @Override        public boolean onTouch(View v, MotionEvent event) {            switch (event.getAction()) {                case MotionEvent.ACTION_DOWN:                    mFirstY = event.getY();                    break;                case MotionEvent.ACTION_MOVE:                    mCurrentY = event.getY();                    if (mCurrentY - mFirstY > mTouchSlop) {                        direction = 0;// down                    } else if (mFirstY - mCurrentY > mTouchSlop) {                        direction = 1;// up                    }                    if (direction == 1) {                        if (mShow) {                            toolbarAnim(1);//show                            mShow = !mShow;                        }                    } else if (direction == 0) {                        if (!mShow) {                            toolbarAnim(0);//hide                            mShow = !mShow;                        }                    }                    break;                case MotionEvent.ACTION_UP:                    break;            }            return false;        }    };

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.scroll_hide);        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();        mToolbar = (Toolbar) findViewById(R.id.toolbar);        mListView = (ListView) findViewById(R.id.listview);        for (int i = 0; i < mStr.length; i++) {            mStr[i] = "Item " + i;        }        View header = new View(this);        header.setLayoutParams(new AbsListView.LayoutParams(                AbsListView.LayoutParams.MATCH_PARENT,                (int) getResources().getDimension( R.dimen.abc_action_bar_default_height_material)));        mListView.addHeaderView(header);        mListView.setAdapter(new ArrayAdapter<String>(                ScrollHideListView.this,                android.R.layout.simple_expandable_list_item_1,                mStr));        mListView.setOnTouchListener(myTouchListener);    }

    private void toolbarAnim(int flag) {        if (mAnimator != null && mAnimator.isRunning()) {            mAnimator.cancel();        }        if (flag == 0) {            mAnimator = ObjectAnimator.ofFloat(mToolbar, "translationY", mToolbar.getTranslationY(), 0);        } else {            mAnimator = ObjectAnimator.ofFloat(mToolbar, "translationY", mToolbar.getTranslationY(), -mToolbar.getHeight());        }        mAnimator.start();    }}

监听listview的OnScrollListener的一般实现

mListView.setOnScrollListener(new AbsListView.OnScrollListener() {    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        switch (scrollState){            case SCROLL_STATE_IDLE://滑动停止时                break;            case SCROLL_STATE_TOUCH_SCROLL://正在滑动时                break;            case SCROLL_STATE_FLING://手指抛动之后listview由于惯性继续滑动                break;        }    }

    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        //firstVisibleItem 第一个可见的item的id        //visibleItemCount 可见的item的总数        //totalItemCount   所有item的总数    }});

获得当前可视的item的位置等信息的便捷方法

mListView.getLastVisiblePosition();//获取可视区域最后一个item的idmListView.getFirstVisiblePosition();//获取可视区域第一个item的id

第五章 Android Scroll分析

1.获取坐标值的各种方法
图片来自Android中的坐标系以及获取坐标的方法

2.实现滑动的基本思想
当触摸view时,系统记下当前触摸点坐标;当手指移动时,系统记下移动后的触摸点坐标,从而获取到相对于前一次坐标点的偏移量,并通过偏移量来修改view的坐标,这样不断重复,从而实现滑动过程。

3.常用的滑动实现方法
(1)修改view的left、top、right和bottom的值:调用layout方法或者offsetLeftAndRight等方法
绝对坐标系下

// 绝对坐标方式@Overridepublic boolean onTouchEvent(MotionEvent event) {    int rawX = (int) (event.getRawX());    int rawY = (int) (event.getRawY());    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            // 记录触摸点坐标            lastX = rawX;            lastY = rawY;            break;        case MotionEvent.ACTION_MOVE:            // 计算偏移量            int offsetX = rawX - lastX;            int offsetY = rawY - lastY;            // 在当前left、top、right、bottom的基础上加上偏移量            layout(getLeft() + offsetX,                    getTop() + offsetY,                    getRight() + offsetX,                    getBottom() + offsetY);            // 重新设置初始坐标            lastX = rawX;            lastY = rawY;            break;    }    return true;}

视图坐标系下

// 视图坐标方式@Overridepublic boolean onTouchEvent(MotionEvent event) {    int x = (int) event.getX();    int y = (int) event.getY();    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            // 记录触摸点坐标            lastX = x;            lastY = y;            break;        case MotionEvent.ACTION_MOVE:            // 计算偏移量            int offsetX = x - lastX;            int offsetY = y - lastY;            // 在当前left、top、right、bottom的基础上加上偏移量            layout(getLeft() + offsetX,                    getTop() + offsetY,                    getRight() + offsetX,                    getBottom() + offsetY);                    //offsetLeftAndRight(offsetX);                    //offsetTopAndBottom(offsetY);            break;    }    return true;}

(2)修改布局参数LayoutParams:修改子view的getLayoutParams或者使用ViewGroup.MarginLayoutParams
子view的getLayoutParams得到的LayoutParams需要和父ViewGroup的Layout类型一致,如果使用ViewGroup.MarginLayoutParams的话那就方便一些,不需要考虑父ViewGroup的具体类型。

@Overridepublic boolean onTouchEvent(MotionEvent event) {    int x = (int) event.getX();    int y = (int) event.getY();    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            // 记录触摸点坐标            lastX = (int) event.getX();            lastY = (int) event.getY();            break;        case MotionEvent.ACTION_MOVE:            // 计算偏移量            int offsetX = x - lastX;            int offsetY = y - lastY;            //ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();            LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();            layoutParams.leftMargin = getLeft() + offsetX;            layoutParams.topMargin = getTop() + offsetY;            setLayoutParams(layoutParams);            break;    }    return true;}

(3)使用scrollTo和scrollBy方法
scrollTo和scrollBy方法移动的是view的content,即让view的内容移动。如果在ViewGroup中使用scrollTo或者scrollBy方法,那么移动的是所有子view。但如果在view中使用,那么移动的将是view的内容,例如TextView,content就是它的文本;ImageView,content就是它的drawable对象。

@Overridepublic boolean onTouchEvent(MotionEvent event) {    int x = (int) event.getX();    int y = (int) event.getY();    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            lastX = (int) event.getX();            lastY = (int) event.getY();            break;        case MotionEvent.ACTION_MOVE:            int offsetX = x - lastX;            int offsetY = y - lastY;            ((View) getParent()).scrollBy(-offsetX, -offsetY);//注意这里需要使用负号进行移动!            break;    }    return true;}

(4)使用Scroller实现平滑效果
前面的滑动都不是平滑的,而Scroller是可以实现平滑效果的,它的实现原理很简单,其实就是不断调用scrollTo和scrollBy方法来实现view的平滑移动,因为人眼的视觉暂留特性看起来就是平滑的。
使用Scroller主要有三个步骤:
1.初始化Scroller对象,一般在view初始化的时候同时初始化scroller;
2.重写view的computeScroll方法,computeScroll方法是不会自动调用的,只能通过invalidate->draw->computeScroll来间接调用,实现循环获取scrollX和scrollY的目的,当移动过程结束之后,Scroller.computeScrollOffset方法会返回false,从而中断循环;
3.调用Scroller.startScroll方法,将起始位置、偏移量以及移动时间(可选)作为参数传递给startScroll方法。

例如,书中给出的例子,子view在被拖动之后会自动平滑移动到原来的位置

private void ininView(Context context) {    setBackgroundColor(Color.BLUE);    // 初始化Scroller    mScroller = new Scroller(context);}

@Overridepublic void computeScroll() {    super.computeScroll();    // 判断Scroller是否执行完毕    if (mScroller.computeScrollOffset()) {        ((View) getParent()).scrollTo( mScroller.getCurrX(), mScroller.getCurrY());        // 通过重绘来不断调用computeScroll        invalidate();//很重要    }}

@Overridepublic boolean onTouchEvent(MotionEvent event) {    int x = (int) event.getX();    int y = (int) event.getY();    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            lastX = (int) event.getX();            lastY = (int) event.getY();            break;        case MotionEvent.ACTION_MOVE:            int offsetX = x - lastX;            int offsetY = y - lastY;            ((View) getParent()).scrollBy(-offsetX, -offsetY);            break;        case MotionEvent.ACTION_UP:            // 手指离开时,执行滑动过程            View viewGroup = ((View) getParent());            mScroller.startScroll( viewGroup.getScrollX(), viewGroup.getScrollY(),                    -viewGroup.getScrollX(), -viewGroup.getScrollY());            invalidate();//很重要            break;    }    return true;}

(5)属性动画 [后面有专门对Android动画分析的部分,此处略过]

(6)使用ViewDragHelper
ViewDragHelper类使用较少,它是support库中DrawerLayout和SlidingPaneLayout内部实现的重要类!
之前读过类似侧边栏菜单的实现代码(SlidingMenu),个人感觉ViewDragHelper其实是更高层次的封装,将这类效果所需的接口暴露出来以简化类似的开发工作,书中给了一个例子,介绍了ViewDragHelper的使用,实现了类似侧边栏菜单的效果

public class DragViewGroup extends FrameLayout {

    private ViewDragHelper mViewDragHelper;    private View mMenuView, mMainView;    private int mWidth;

    public DragViewGroup(Context context) {        super(context);        initView();    }

    public DragViewGroup(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }

    public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView();    }

    @Override    protected void onFinishInflate() {        super.onFinishInflate();        mMenuView = getChildAt(0);        mMainView = getChildAt(1);    }

    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mWidth = mMenuView.getMeasuredWidth();    }

    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return mViewDragHelper.shouldInterceptTouchEvent(ev);    }

    @Override    public boolean onTouchEvent(MotionEvent event) {        //将触摸事件传递给ViewDragHelper,此操作必不可少        mViewDragHelper.processTouchEvent(event);        return true;    }

    private void initView() {        mViewDragHelper = ViewDragHelper.create(this, callback);    }

    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {

                // 何时开始检测触摸事件                @Override                public boolean tryCaptureView(View child, int pointerId) {                    //如果当前触摸的child是mMainView时开始检测                    return mMainView == child;                }

                // 触摸到View后回调                @Override                public void onViewCaptured(View capturedChild, int activePointerId) {                    super.onViewCaptured(capturedChild, activePointerId);                }

                // 当拖拽状态改变,比如idle,dragging                @Override                public void onViewDragStateChanged(int state) {                    super.onViewDragStateChanged(state);                }

                // 当位置改变的时候调用,常用与滑动时更改scale等                @Override                public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {                    super.onViewPositionChanged(changedView, left, top, dx, dy);                }

                // 处理垂直滑动                @Override                public int clampViewPositionVertical(View child, int top, int dy) {                    return 0;                }

                // 处理水平滑动                @Override                public int clampViewPositionHorizontal(View child, int left, int dx) {                    return left;                }

                // 拖动结束后调用                @Override                public void onViewReleased(View releasedChild, float xvel, float yvel) {                    super.onViewReleased(releasedChild, xvel, yvel);                    //手指抬起后缓慢移动到指定位置                    if (mMainView.getLeft() < 500) {                        //关闭菜单,相当于Scroller的startScroll方法                        mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);                    } else {                        //打开菜单                        mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);                    }                }            };

    @Override    public void computeScroll() {        if (mViewDragHelper.continueSettling(true)) {            ViewCompat.postInvalidateOnAnimation(this);        }    }}
时间: 2024-12-20 19:42:33

第二章 控件架构与自定义控件详解 + ListView使用技巧 + Scroll分析的相关文章

Android控件架构与自定义控件详解(二)——自定义View

在自定义View时,我们通常会去重写onDraw()方法来绘制View的显示内容.如果该View还需要使用wrap_content属性,那么还必须重写onMeasure()方法.另外,通过自定义attrs属性,还可以设置新的属性配置值. 在View中通常有一些比较重要的回调方法. onFinishInflate():从XML加载组件后回调. onSizeChanged(;:组件大小改变时. onMeasure():回调该方法来进行测量. onLayout():回调该方法来确定显示的位置. onT

Android 控件架构与自定义控件详解

架构: PhoneWindow 将一个 DecorView 设置为整个应用窗口的根 View,这里面所有 View 的监听事件,都通过 WindowManagerService 来接收.DecorView 分为 TitleView 和 ContentView,ContentView 是一个 ID 为 content 的 FrameLayout 在 onCreate() 方法中调用 setContentView() 方法后,ActivityManagerService 会回调onResume() 

Android控件架构与自定义控件详解

基于 <android 群英传 >的读书笔记 View的测量-onMeasure() 测量的模式可以有以下三种: EXACTLY 即精确值模式,当我们将控件的layout_width属性或layout_height属性指定为具体参数值时,系统使用的就是EXACTLY AT_MOST 即最大值模式,当控件的layout_width属性或layout_hright属性是warp_content时,控件大小一般随着控件的子控件或内容的变化而变化,此时控件尺寸只要不超过父控件允许的尺寸即可 UNSPE

ListView(1)控件架构与ArrayAdapter详解

ListView是Android开发中比较常用的一个组件,它以列表的形式展示信息,并能根据信息的长度自适应显示.比如说我们手机里的通讯录就用到了ListView显示联系人信息.在大量的场合下,我们都需要使用这个控件.虽然在Android 5.0时代,RecyclerView在很多地方都在逐渐取代ListView,但ListView的使用范围依然非常的广泛.我们也不能跳过ListView直接去学习RecyclerView,对ListView的透彻理解是十分有必要的. 首先来看ListView在Vi

Android读书笔记3:控件架构以及自定义控件

安卓平台上这么多多姿多彩的控件是怎么制作出来的?有系统自定义的,也有开发者在系统的基础上进行自定义的.但是他们一定都遵循一定的规则,那就是android对于控件的架构设计. 言简意赅地说下:1.所有的控件都有 共同的父类,要么父类是View,要么父类是ViewGroup,顾名思义,后者意思是View的群组,前者是单个控件.有一个概念叫做控件树,即 所有的控件如果画成结构图,一定是一个树状结构图,我们activity里面用的findViewById就是按照树的深度优先遍历来查找对应的view( 从

android第二章控件1

1.Activity:用于存放各个显示控件,是android的基本组成 2.Activity常用方法:public final View findViewById(int id) 根据组件的id取得组件对象       public void setContentView(int layoutResIdd)  设置显示组件 3.设置控件的ID值:格式:@+id/ID值 4.TextView标签(标签按钮): 4.1常用属性:1:android:text //定义组件的显示文字 2:  textC

android第二章控件2

Button (按钮) EditText(编辑框)                 android: password=""                                     //是否以密文方式显示             android: numeric="integer"                           //只能输入数字             android: hint="**"          

OpenLayers 之 控件(control)详解

每一个地图应用都应该有一些工具方便用户控制地图的行为,比如缩放,全屏,坐标控件等等,在 OpenLayers 中怎么添加这些工具呢?下面我给大家介绍一下 OpenLayers 中包含的控件种类,并介绍其使用方法.对控件的定制化,和对 OpenLayers 增加控件和优化控件等超出了本文范围. 一.control 类 OpenLayers 中的控件是由 control 类定义的,这是一个虚基类,不负责实例化特定的控件,它的主要作用是让其他具体的种类的控件类实现继承.OpenLayers 中包含的控

使用appearance proxy定制控件的默认外观(详解)

控件的外观,受到tint color,background image, background color等属性的共同影响,通常要修改某个控件对象的外观,就去调用上面属性的相关setter方法(或者其他可以修改它们的方法)就可以了.但是,如果希望整个app中的控件都保持一致的风格,比如所有button的风格(指的是大小,背景图,形状等)都一样,那么一个一个去重复设置每个button的风格,就显得太麻烦了.如果可以给Button类设定一个默认外观,就方便多了.appearance proxy就可以