常规&高级UI编程

199 阅读4分钟

UI组件

常规UI组件

常规UI组件大多由Android Framework中的android.widget这个package提供。

Android的常规UI组件包括文本组件(TextView)、图片组件(ImageView)、按钮组件(Button)、输入框组件(EditText)、复选框组件(CheckBox)、单选按钮(RadioButton)等等。

其中他们也有一些通用属性,例如id,height,width,margin,padding等等,同样也有一些通用方法,例如setId,setLayoutParams,setPadding等等。

这里就不多赘述了。

高级UI组件

高级UI组件大多是ViewGroup,比起常规UI组件有更多的功能。

Android的高级UI组件包括滑动组件(ScrollView)、列表组件(ListView/RecyclerView)、下拉刷新组件(PullToRefresh)、分页组件(ViewPager)、布局组件(LinearLayout/RelativeLayout/..)等等。

View与ViewGroup的关系

ViewGroup可以嵌套另外的ViewGroup或者View,而View不能嵌套。

UI布局

关于布局我们直接来看实例。

LinearLayout

LinearLayout 又称线性布局,该布局应该是 Android 视图设计中最经常使用的布局。该布局可以使放入其中的组件以水平方式或者垂直方式整齐排列,通过 android:orientation 属性指定具体的排列方式,通过 weight 属性设置每个组件在布局中所占的比重。

先看示例效果

20220729172645.png

代码如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="To" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Subject" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="top"
        android:hint="Message"/>

    <Button
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="Send"/>
</LinearLayout>

RelativeLayout

RelativeLayout 又称相对布局。从名称上可以看出,这种布局方式是以一种让组件以相对于容器或者相对于容器中的另一个组件的相对位置进行放置的布局方式。

先看示例效果

20220729185744.png

代码如下

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp">

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Reminder name"/>

    <Spinner
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/name"
        android:layout_alignParentLeft="true"
        android:layout_toLeftOf="@+id/times"
        android:contentDescription="Date"
        android:entries="@array/dates"
        android:spinnerMode="dropdown" />

    <Spinner
        android:id="@+id/times"
        android:layout_width="96dp"
        android:layout_height="wrap_content"
        android:layout_below="@id/name"
        android:layout_alignParentRight="true"
        android:entries="@array/times"
        android:spinnerMode="dropdown" />

    <Button
        android:id="@+id/btn"
        android:layout_width="96dp"
        android:layout_height="wrap_content"
        android:layout_below="@id/times"
        android:layout_alignParentRight="true"
        android:text="Done" />
</RelativeLayout>

ConstraintLayout

ConstraintLayout允许以灵活的方式定位和调整子组件的大小。 与RelativeLayout类似,所有的视图都是根据兄弟视图和父布局之间的关系来布局的,但是它比RelativeLayout更灵活,并且更易于在Android Studio的布局编辑器中使用。

先看示例效果

PD{7(5_GPX9)WU1XFSTV4RP.png

代码如下

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <ImageView
        android:id="@+id/iv_poster1"
        android:layout_width="100dp"
        android:layout_height="200dp"
        android:scaleType="centerCrop"
        android:src="@drawable/poster1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <ImageView
        android:id="@+id/iv_poster2"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/poster2"
        app:layout_constraintBottom_toBottomOf="@+id/iv_poster1"
        app:layout_constraintLeft_toRightOf="@id/iv_poster1"
        app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

UI交互

我们给之前RelativeLayout的按钮加一个点击事件,先来看效果。

G00G6%$K9.png

具体代码如下

    Button button = findViewById(R.id.btn);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(MainActivity.this,"触发按钮点击事件",Toast.LENGTH_SHORT).show();
            playAnimation();
        }
    });

整体就是做了两件事情,一是获取对应的View实例,二是添加相应的监听器。

除此之外还有不少常用的交互事件监听器,例如onClick,onLongClick,onFocusChange,onKey,onTouch。

另外,触摸事件的分发也是从Acivity到ViewGroup再到View。

UI动画

UI动画分为三种,帧动画、补间动画和属性动画。我们这里主要拿帧动画和补间动画举例。

帧动画

帧动画就是顺序播放一组预先定义好的图片,就类似于我们观看视频,就是一张一张的图片连续播放。

先来看样例,这里是做到了一个点击DONE按钮就加载转圈动画的效果。

M2X%)Q)WUQFF{0X3CB0_{DS.png

关键代码如下

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/loading_1" android:duration="20"/>
    <item android:drawable="@drawable/loading_2" android:duration="20"/>
    <item android:drawable="@drawable/loading_3" android:duration="20"/>
    ....
    <item android:drawable="@drawable/loading_60" android:duration="20"/>
</animation-list>


ImageView mImageView;
private void playAnimation(){
    mImageView = findViewById(R.id.image);
    mImageView.setImageResource(R.drawable.frame_animation);
    AnimationDrawable animationDrawable = (AnimationDrawable) mImageView.getDrawable();
    animationDrawable.start();
}

补间动画

补间动画是Android一开始就提供的比较原始的动画,主要支持四种效果:平移、缩放、旋转、透明度变化(渐变) 四种基本效果,我们可以在这四种基础效果的基础上,选择其中的几种进行组合。

先来看样例,这里是把四种补间动画组合呈现了。

B~(8R~JO_JPRUA86A2KNW3F.png

来看关键代码

public void tweenedAnimation(View view) {
    AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);
    alphaAnimation.setDuration(3000);

    RotateAnimation rotateAnimation = new RotateAnimation(0,360, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    rotateAnimation.setDuration(3000);

    ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    scaleAnimation.setDuration(3000);

    TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,1,Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,1);
    translateAnimation.setDuration(3000);

    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(alphaAnimation);
    animationSet.addAnimation(rotateAnimation);
    animationSet.addAnimation(scaleAnimation);
    animationSet.addAnimation(translateAnimation);
    view.startAnimation(animationSet);
}

属性动画

属性动画可以看作是增强版的补间动画,与补间动画的不同之处体现在:

  • 补间动画只能定义两个关键帧在透明、旋转、位移和倾斜这四个属性的变换,但是属性动画可以定义任何属性的变化。
  • 补间动画只能对 UI 组件执行动画,但属性动画可以对任何对象执行动画。

自定义UI

结合上面的内容,我们也就可以去实现一个自定义的UI控件了。例如下面的自定义开关控件:github.com/zcweng/Swit… 就是一个模仿iOS的开关控件。

21879.gif

要制作一个自定义UI控件,需要做以下五件事情,首先要创建View,然后处理布局,接下来要处理绘制,随后处理交互,最后再处理动画。

这里就不多赘述了。