常见的布局
-
线性布局(LinearLayout):按照垂直或者水平方向布局组件。
-
帧布局(FrameLayout): 组件从屏幕的左上角坐标布局组件。
-
表格布局(TableLayout):按照行列方式布局组件。
-
相对布局(RelativeLayout):相对其他组件的布局方式。
-
绝对布局(AbsoluteLayout):按照绝对坐标来布局组件。
我们在Activity中调用setContentView时有下面3中方式:
voidsetContentView(int layoutResID); 从layout资源文件设置显示view
voidsetContentView(View view); 从已经创建好的view设置
voidsetContentView(View view, ViewGroup.LayoutParams params); 从已经创建好的view设置,并制定布局。
ScrollView滚动视图(开发中最常用的视图)
ScrollView:垂直方向的滚动视图;垂直方向滚动时,layout_width属性值设置为match——parent,layout_height属性值设置为wrap_content。
HorizontalScrollView:水平方向滚动视图,水平方向滚动时:layout_width属性值设置为wrap_content,layout_height属性值设置为match_parent。
多线程相关:handler,sendMessage,Thread,
文件读取:files,直接cat这个proc文件夹下面的结点 ,并输出数据。
gridView和Gridlayout的区别
GridView是一种适配器布局,它的继承关系是ViewGroup–>AdapterView–>AbsListView–>GridView,他是从一个adapter中取出内容填充到GridView中的每一个子View。
GridLayout是一个布局,它大大简化了对复杂布局的处理,提高了性能。他直接继承自ViewGroup,和LinearLayout这种是类似的。
我感觉GridView和GridLayout就和ListView和LinearLayout的区别类似。
那么什么时候用GridView什么时候用GridLayout呢?
类似九宫格这种可以采用GridView。但是像Android系统自带的计算器的界面就不可能通过GridView实现,因为有些按键不一样大,如果使用GridLayout实现那就很简单了。
另外GridLayout比RelativeLayout更好用,例如:
一个登陆界面举例:登陆界面应该有登陆界面的标题、用户名和密码的label和输入框及确认登陆按钮,如果用相对布局实现肯定是可以实现的,需要相对布局里面控件与控件间的位置关系就复杂繁琐了。Android框架在将你定义的相对布局xml文件渲染给屏幕的时候,需要处理这么复杂的控件间的关系,肯定性能会受一定程度的影响。当然,用相对布局会比用多个线性布局嵌套性能要好一些。
但是,用GridLayout的话,渲染性能会比用相对布局好很多。因为GridLayout中的控件间的位置关系没那么复杂,就是指定将某个控件放到某行某列,占几行或占几列,布局xml写起来也非常简洁。
网格视图
GridView是一个ViewGroup,它以二维可滚动网格显示项目.使用ListAdapter将网格项自动插入到布局中.
网格布局
将子项放在矩形网格中的布局.
网格由一组无限细线组成,将观察区域分隔成单元格.在整个API中,网格线由网格索引引用.具有N列的网格具有从0到N(包括0和N)的N + 1个网格索引.无论如何配置GridLayout,网格索引0固定到容器的前沿,并且网格索引N固定到其后边缘(在考虑填充之后).
安卓基础View详解
View的继承关系
==Android系统中,任何可视化控件都需要从android.viewView类继承。==而任何从Android.view.View继承的类都可以称为视图(View)。Android的中的绝大部分UI组件都放在Android.widget包及其子包下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZX6vs1H-1661744075165)(C:\Users\29933\AppData\Roaming\Typora\typora-user-images\image-20220829091553561.png)]
从上图看,还有很多布局没有看到,上图仅仅是android.widge包的,还有其他视图,虽然也继承View,但是他们不属于android.widge包。
- RecycleView:继承ViewGroup,但是属于androidx.recyvlerview.widge包的。
- ConstrainLayout继承ViewGroup,但是属于androidx.constraintlayout.widge包的。
- 还有很多自定义控件。
安卓中的属兔类可以分为3种:布局类(Layout),视图容器(ViewGroup),视图类(TextView)。
这三种都是android.view.View的子类,ViewGroup是一个容器,该类也是view的重要子类,所有的布局类和视图容器类都似乎ViewGroup的子类,而视图类直接继承自View类。下图描述了View,ViewGroup,视图容器类及视图类的继承关系。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySADcqhY-1661744075167)(C:\Users\29933\AppData\Roaming\Typora\typora-user-images\image-20220829093040296.png)]
从上图可以看出:
- Button、TextView、EditText都是视图类,TextView是Button和EditText的父类,TextView直接继承自View类。
- GridView和ListView是ViewGroup的子类,但并不是直接子类,GridView、ListView继承自AbsListView继承自AdapterView继承自ViewGroup,从而形成了视图容器类的层次结构。
- 布局视图虽然也属于容器视图,但由于布局视图具有排版功能,所以将这类视图置为布局类。
对于一个Android应用的图形用户界面来说,ViewGroup作为容器来装其他组件,而ViewGroup里除了可以包含普通View组件之外,还可以再次包含ViewGroup组件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IvIqBfJF-1661744075167)(C:\Users\29933\AppData\Roaming\Typora\typora-user-images\image-20220829094739258.png)]
创建View对象
使用XML布局定义View,再用代码控制View
XML布局文件是Android系统中定义视图的常用方法,所有的XML布局文件必须保存在res/layout目录中。XML布局文件的命名及定义需要注意如下几点:
- XML布局文件的扩展名必须是xml。
- 由于aapt会根据每一个XML布局文件名在R类的内嵌类中生成一个int类型的变量,这个变量名就是XML布局文件名,因此,XML布局文件名(不包含扩展名)必须符合Java变量名的命名规则,例如,XML布局文件名不能以数字开头。
- 每一个XML布局文件的根节点可以是任意的视图标签,如< LinearLayout >,< TextView >。
- XML布局文件的根节点必须包含android命名空间,而且命名空间的值必须是android=“http://schemas.android.com/apk/res/android”。
- 为XML布局文件中的标签指定ID时需要使用这样的格式:@+id/tv_xml,其实@+id就是在R.java文件里新增一个id名称,在同一个xml文件中确保ID唯一。
- 由于每一个视图ID都会在R.id类中生成与之相对应的变量,因此,视图ID的值也要符合Java变量的命名规则,这一点与XML布局文件名的命名规则相同。
举例
1.创建activity_view.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_666666"
android:orientation="vertical">
<TextView
android:id="@+id/tv_xml"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dimen_20"
android:background="@color/color_188FFF"
android:padding="@dimen/dimen_10"
android:text="XML设置TextView"
android:textColor="@color/white"
android:textSize="@dimen/text_size_18" />
<Button
android:id="@+id/btn_xml"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dimen_20"
android:background="@color/color_188FFF"
android:padding="@dimen/dimen_10"
android:text="按钮"
android:textColor="@color/white"
android:textSize="@dimen/text_size_18" />
</LinearLayout>
2.加载布局文件、关联控件
如果要使用上面的XML布局文件(activity_view.xml),通常需要在onCreate方法中使用setContentView方法指定XML布局文件的资源lD,并获取在activity_view.xml文件中定义的某个View,代码如下:
public class ViewActivity extends AppCompatActivity{
private Button btnXml;
private TextView tvXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//加载布局文件
setContentView(R.layout.activity_view);
//如果想获得在activity_view.xml文件中定义的某个View
//关联控件:R.id.tv_xml是tvXml的ID,确保这个ID在R.layout.activity_view中
tvXml = findViewById(R.id.tv_xml);
//关联控件:R.id.btn_xml是btnXml的ID,确保这个ID在R.layout.activity_view中
btnXml = findViewById(R.id.btn_xml);
}
}
3.在获得XML布局文件中的视图对象时需要注意下面几点:
- 先使用setContentView方法装载XML布局文件,再使用findViewByld方法,否则findViewByld方法会由于未找到控件而产生空指针异常,导致应用崩溃。
- 虽然所有的XML布局文件中的视图ID都在R.id类中生成了相应的变量,但使用findViewByld方法只能获得已经装载的XML布局文件中的视图对象。
- 例,activity_view.xml中TextView的对应R.id.tv_xml;
- 其他XML文件中有TextView的R.id.tv_shuaiciid,tv_shuaici不在activity_view.xml中如果使用 tvXml = findViewById(R.id.tv_shuaici);
- 结果应用崩溃。原因:在activity_view.xml中找不到ID为tv_shuaici的视图对象。
4.用代码控制视图
虽然使用XML布局文件可以非常方便地对控件进行布局,但若想控制这些控件的行为,仍然需要编写Java代码。在上面介绍了使用findViewByld方法获得指定的视图对象,当获得视图对象后,就可以使用代码来控制这些视图对象了。例如,下面的代码获得了一个TextView对象,并修改了TextView的文本。
TextView tvXml = findViewById(R.id.tv_xml);
//直接使用字符串来修改TextView的文本
tvXml.setText("帅次");
//使用字符串资源(res/values/strings.xml)
//其中R.string.str_tv_shuaici是字符串资源ID,系统会使用这个ID对应的字符串设置TextView的文本。
tvXml.setText(R.string.str_tv_shuaici);
选择其中一样即可,如果同时设置,最后一次设置为最终结果。
使用代码的方式来创建View对象
在更高级的Android应用中,往往需要动态添加视图。要实现这个功能,最重要的是获得当前的视图容器对象,这个容器对象所对应的类需要继承ViewGroup类。
将其他的视图添加到当前的容器视图中需要如下几步:
- 第1步,获得当前的容器视图对象;
- 第2步,获得或创建待添加的视图对象;
- 第3步,将相应的视图对象添加到容器视图中。
总结
- 实际上不管使用那种方式,他们创建Android用户界面行为的本质是完全一样的。大部分时候,设置UI组件的XML属性还有对应的方法。
- 对于View类而言,它是所有UI组件的基类,因此它包含的XML属性和方法是所有组件都可以使用的。
自定义View
为什么要自定义View
Android系统提供了一系列的原生控件,但这些原生控件并不能够满足我们的需求时,我们就需要自定义View了。
自定义View的基本方法
自定义View的最基本的三个方法分别是: onMeasure()、onLayout()、onDraw();
View在Activity中显示出来,要经历测量、布局和绘制三个步骤,分别对应三个动作:measure、layout和draw。
- 测量:onMeasure()决定View的大小;
- 布局:onLayout()决定View在ViewGroup中的位置;
- 绘制:onDraw()决定绘制这个View。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sjcfJ8J4-1661744075167)(C:\Users\29933\AppData\Roaming\Typora\typora-user-images\image-20220829095356737.png)]
需要用到的两个对象
- Canvas(画布),可在画布上面绘制东西,绘制的内容取决于所调用的方法。如drawCircle方法,用来绘制圆形,需要我们传入圆心的x和y坐标,以及圆的半径。
- Paint(画笔),用来告诉画布,如何绘制那些要被绘制的对象。
这两个方法暂时了解就行,如果拓展开,这不够写,后面可能会针对这两个对象单独拉一个章节出来。
自绘控件View实例
1、直接继承View类
自绘View控件时,最主要工作就是绘制出丰富的内容,这一过程是在重写的onDraw方法中实现的。由于是View,它没有子控件了,所以重写onLayout没有意义。onMeasure的方法可以根据自己的需要来决定是否需要重写,很多情况下,不重写该方法并不影响正常的绘制。
/**
* 创建人:scc
* 功能描述:自定义View
*/
public class CustomView extends View {
private Paint paint;
//从代码创建视图时使用的简单构造函数。
public CustomView(Context context) {
super(context);
}
//从XML使用视图时调用的构造函数。
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
//View的绘制工作
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//实例化画笔对象
paint = new Paint();
//给画笔设置颜色
paint.setColor(Color.RED);
//设置画笔属性
//paint.setStyle(Paint.Style.FILL);//画笔属性是实心圆
paint.setStyle(Paint.Style.STROKE);//画笔属性是空心圆
paint.setStrokeWidth(10);//设置画笔粗细
//cx:圆心的x坐标;cy:圆心的y坐标;参数三:圆的半径;参数四:定义好的画笔
canvas.drawCircle(getWidth() / 4, getHeight() / 4, 150, paint);
}
}
2、在布局 XML 文件中使用自定义View
<com.scc.demo.view.CustomView
android:id="@+id/view_circle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
3、实现效果:
性能优化
在自定义View时需要注意,避免犯以下的性能错误:
- 在非必要时,对View进行重绘。
- 绘制一些不被用户所看到的的像素,也就是过度绘制。(被覆盖的地方)
- 在绘制期间做了一些非必要的操作,导致内存资源的消耗。
可进一步了解和优化:
- View.invalite()是最最广泛的使用操作,因为在任何时候都是刷新和更新视图最快的方式。
在自定义View时要小心避免调用非必要的方法,因为这样会导致重复强行绘制整个视图层级,消耗宝贵的帧绘制周期。检查清楚View.invalite()和View.requestLayout()方法调用时间位置,因为这会影响整个UI,导致GPU和它的帧速率变慢。
- 避免过渡重绘。为了避免过渡重绘,我们可以利用Canvas方法,只绘制控件中所需要的部分。整个一般在重叠部分或控件时特别有用。相应的方法是Canvas.clipRect()(指定要被绘制的区域);
- 在实现View.onDraw()方法中,不应该在方法内及调用的方法中进行任何的对象分配。在该方法中进行对象分配,对象会被创建和初始化。而当View.onDraw()方法执行完毕时。垃圾回收器会释放内存。如果View带动画,那么View在一秒内会被重绘60次。所以要避免在View.onDraw()方法中分配内存。
永远不要在View.onDraw()方法中及调用的方法中进行内存分配,避免带来负担。垃圾回收器多次释放内存,会导致卡顿。最好的方式就是在View被首次创建出来时,实例化这些对象。
到这里View基本差不多了,还有其他属性、方法、事件等,在后面的TexView、Button、Layout等中慢慢了解。