Android View 体系(1):View和ViewGroup体系结构

作者:陆金龙    发表时间:2018-03-03 14:32   


1 View和ViewGroup体系结构

1.1 View体系概要

Android的UI界面由View和ViewGroup及其子类组合而成。

View是所有UI组件的父类,其子类称为组件(Widget)。

ViewGroup是布局管理器,本身继承自View类,其子类称为布局(Layout)。

View的子类体系结构如下图:

1.2 View子类介绍

1.2.1 ViewGroup

ViewGroup的直接子类有:FrameLayout,LinearLayout,RelativeLayout,AbsoluteLayout,AdapterView,FragmentBreadCrumbs,SlidingDrawer
ViewGroup派生出的间接子类有:AbsListView, AbsSpinner, AdapterViewAnimator, AdapterViewFlipper, AppWidgetHostView, CalendarView, DatePicker, DialerFilter, ExpandableListView, Gallery, GestureOverlayView,GridView,HorizontalScrollView, ImageSwitcher,ListView,

 

1.2.2 TextView

为文本框设置多种颜色 :

TextView tv =(TextView)findViewById(R.id.tv_show);  

       SpannableStringBuilder style = new SpannableStringBuilder("蓝色:默认(红色)");  

       style.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);  

       style.setSpan(new ForegroundColorSpan(Color.RED), 6, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);  

       textView.setText(style);  

 

一行文字多种颜色:

String str = "<font color='red'>红色</font>"  + "<font color= 'green'>绿色</font>";  

TextView tv = new TextView(this);  

 tv.setText(Html.fromHtml(str));  

 

1.2.3 ImageView

scaltype

CENTER  按原尺寸居中显示,图片长/宽超过View时截取图片居中部分

CENTER_CROP (当长或宽小于View时)按比例扩大尺寸居中显示,使图片长/宽不小于View的长/宽(可超出View)

CENTER_INSIDE (当长或宽大于View时)按比例缩小完整居中显示,使得图片长/宽不大于View长/宽

FIT_CENTER / FIT_CENTER  (不管长或宽是大于或小于View)把图片按比例扩大/缩小到View的宽或高居中显示。

FIT_END / fitEnd   把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置

FIT_START / fitStart  把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置

FIT_XY / fitXY  把图片不按比例扩大/缩小到View的大小显示

MATRIX / matrix 用矩阵来绘制,动态缩小放大图片来显示。

 

1.2.4 SurfaceView

SurfaceView就是在窗口上挖一个洞,它就是显示在这个洞里,其他的View是显示在窗口上。SurfaceView属于View的子类 它是专门为制作游戏而产生的,支持OpenGL ES库,2D和3D的效果都可以实现。

SurfaceView允许其他线程更新视图对象(执行绘制方法),而其他的View不允许这么做,它只允许UI线程更新视图对象。

 

适用于被动更新,例如频繁地刷新。

适用于子线程来主动刷新界面,比如一个小人一直跑动的效果,需要子线程不停的重绘人的状态,避免阻塞UI线程。

使用surfaceView,实现SurfaceHolder.Callback接口(SurfaceView的改变 、SurfaceView的创建 、SurfaceView 销毁):

//Surface的大小发生改变时调用。

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height);

//Surface创建时触发,一般在这里调用画面的线程

public void surfaceCreated(SurfaceHolder holder);

//销毁时触发,一般在这里将画面的线程停止、释放。

public void surfaceDestroyed(SurfaceHolder holder);

 

例:频繁拍照功能

<SurfaceView android:id="@+id/surfaceView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"/>

 

CameraMgr cameraMgr = new CameraMgr(this, R.id.surfaceView, true);

cameraMgr.SetEventListener(this);

cameraMgr.initCamera();

 

CameraMgr类代码清单:

public class CameraMgr {

    private String TAG = "CameraMgr";

    private Activity mContext;

    private Camera mCamera;

 

    private SurfaceView mPreView;

    private SurfaceHolder mHolder;

    private int cameraPosition = 1;//0代表前置摄像头,1代表后置摄像头

    Camera.PictureCallback pictureCallback;

 

    public CameraMgr(Activity context, @IdRes int surfaceViewId, boolean isFacingBack) {

        this.mContext = context;

        mPreView = (SurfaceView) mContext.findViewById(surfaceViewId);

        this.cameraPosition = isFacingBack ? 0 : 1;

    }

 

    public void SetEventListener(Camera.PictureCallback listener) {

        this.pictureCallback = listener;

    }

 

    private boolean checkCamera(Context context) {

        // 可修改参数来判断设备是否支持Camera等功能

        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {

            return true; // 支持Camera功能

        } else {

            return false;

        }

    }

 

    private void getCamera() {

        int cameraCount = Camera.getNumberOfCameras();//得到摄像头的个数

        cameraInfo = new Camera.CameraInfo();

        for (int i = 0; i < cameraCount; i++) {

            Camera.getCameraInfo(i, cameraInfo);//得到每一个摄像头的信息

            if (cameraPosition == 0) {

                //前置

                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {

                    mCamera = Camera.open(i);

                    Log.e("mCamera", mCamera == null ? "失败" : "成功");

                    break;

                }

            } else {

                //后置

                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {

                    mCamera = Camera.open(i);

                    break;

                }

            }

        }

    }

 

    Camera.CameraInfo cameraInfo;

 

    public boolean initCamera() {

        if (checkCamera(mContext)) {

            getCamera();

            takePreview();

            return true;

        } else {

            return false;

        }

    }

 

    public void resetCamera() {

        takePreview();

    }

 

    private void takePreview() {

        mHolder = mPreView.getHolder();

        mHolder.addCallback(new SurfaceHolder.Callback() {

            @Override

            public void surfaceCreated(SurfaceHolder holder) {

                if (mCamera != null && mHolder != null) {

                    setStartPreview(mCamera, mHolder);

                }

            }

 

            @Override

            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

                if (mCamera != null) {

                    mCamera.stopPreview();

                    if (mHolder != null) {

                        setStartPreview(mCamera, mHolder);

                    }

                }

            }

 

            @Override

            public void surfaceDestroyed(SurfaceHolder holder) {

                releaseCamera();

            }

        }); // 实现SurfaceHolder.Callback接口

        mCamera.autoFocus(null);

        setStartPreview(mCamera, mHolder);//启动预览

    }

 

private void setStartPreview(Camera camera, SurfaceHolder holder) {

        try {

// 设置显示取景画面到surfaceview

            camera.setPreviewDisplay(holder);

            // 设置相机参数

Camera.Parameters parameters = camera.getParameters();

            parameters.setPictureFormat(ImageFormat.JPEG); // 相片保存的格式

            parameters.setPictureSize(540, 960); // 相片保存的尺寸,因为是横屏所以长>高

            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // 自动对焦

            if(mContext.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {

                parameters.set("orientation", "portrait");

                camera.setDisplayOrientation(90);

            } else {

                parameters.set("orientation", "landscape");

                camera.setDisplayOrientation(0);

            }

    // 启动预览

            camera.startPreview();

        } catch (IOException e) {

            Log.e(TAG, "setStartPreview: setPreviewDisplay error");

        }

    }

 

    public void releaseCamera() {

        if (mCamera != null) {

            mCamera.setPreviewCallback(null);

            mCamera.stopPreview();//停掉原来摄像头的预览

            mCamera.release();//释放资源

            mCamera = null;//取消原来摄像头

        }

    }

 

    public void takePicture() {

        if (mCamera == null) {

            return;

        }

        Camera.Parameters parameters = mCamera.getParameters();

        parameters.setPictureFormat(ImageFormat.JPEG); // 相片保存的格式

        parameters.setPictureSize(540, 960); // 相片保存的尺寸,因为是横屏所以长>高

        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // 自动对焦

        parameters.set("orientation", "portrait");

        mCamera.setDisplayOrientation(90);//拍照旋转无效

        try {

            mCamera.takePicture(null, null, pictureCallback);// 拍照

        } catch (Exception e) {

            Log.e("takePicture", e.getMessage());

        }

    }

}

 

1.2.5 ProgressBar

style属性

· Widget.ProgressBar.Horizontal //水平ProgressBar样式

· Widget.ProgressBar.Small  //小环形进度条

· Widget.ProgressBar.Large   //大环形进度条

· Widget.ProgressBar.Inverse

· Widget.ProgressBar.Small.Inverse

· Widget.ProgressBar.Large.Inverse

indeterminate 属性:

android:indeterminate 就是关于设置不精确的属性,设置为true的话,滚动条的当前值会自动在最小到最大值之间来回移动,形成这样一个动画效果

进度属性

ProgressBar有两个进度,一个是android:progress(第一显示进度),另一个是android:secondaryProgress(第二显示进度),如在看网络视频时候都会有一个缓存的进度以及一个播放的进度。

常用方法:

· setProgress(int) 设置第一进度

· setSecondaryProgress(int) 设置第二进度

· getProgress() 获取第一进度

· getSecondaryProgress() 获取第二进度

· incrementProgress(int) 增加或减少第一进度

· incrementSecondaryProgress(int) 增加或减少第二进度

· getMax() 获取最大进度

 

1.2.6 AnalogClock

仪表盘时钟

1.2.7 KeyboardView

键盘控件,用于编写键盘。为了安全而APP自带键盘,比如手机银行。

1.2.8 ViewStub

轻量级的View,没有尺寸,不绘制任何东西,绘制或者移除时省时。

实现View的延迟加载,在需要的时候才加载View,起到节约内存提高性能的作用。

 

为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向的布局才会被Inflate实例化。

 

用法:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

    <Button

        android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content"  android:text="点击加载ViewStub" />

    <ViewStub

        android:id="@+id/view_stub" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout="@layout/view_toload" />

  </LinearLayout>

 

Button button = (Button) findViewById(R.id.button);    

button.setOnClickListener(new View.OnClickListener() {

        @Override

        public void onClick(View v) {

            View view = ((ViewStub)findViewById(R.id.view_stub)).inflate();

            TextView tv = (TextView) view.findViewById(R.id.tv_test);

            tv.setText("view_toload中的TextView");

    }

});

 

1.3 Android坐标系

1.3.1 View获取自身宽高

getHeight():获取View自身高度

getWidth():获取View自身宽度

View自身坐标

通过如下方法可以获得View到其父控件(ViewGroup)的距离:

getTop():获取View自身顶边到其父布局顶边的距离

getLeft():获取View自身左边到其父布局左边的距离

getRight():获取View自身右边到其父布局左边的距离

getBottom():获取View自身底边到其父布局顶边的距离

MotionEvent提供的方法

MotionEvent提供了各种获取焦点坐标的方法:

getX():获取点击事件距离控件左边的距离

getY():获取点击事件距离控件顶边的距离

getRawX():获取点击事件距离整个屏幕左边距离,即绝对坐标

getRawY():获取点击事件距离整个屏幕顶边的的距离,即绝对坐标