天天看点

自定义View实战第(一)课:仿QQ空间顶部条背景变化效果

一、前期基础知识储备

《Android开发艺术探索》中对自定义View的讲解非常精彩,而笔者之前自定义View精炼详解的四节系列课程的讲解,意外的和书中的知识点对应上了,个人感到非常高兴。

我们来回顾一下:

《自定义View精炼详解第(一)课:基础理论部分和简单小实现》里面自定义View的示例是一个“背景可变化的按钮”,自定义View的方式是继承自现有控件。

不需要自己支持wrap_content和padding

《自定义View精炼详解第(二)课:自定义属性部分和入门级实现》里面自定义View的示例是一个“属性可变的标题栏”,自定义View的方式是继承自现有布局(1)。

不需要自己处理ViewGroup的测量和布局过程

《自定义View精炼详解第(三)课:onDraw()方法解析和小白级案例实现》里面自定义的示例是一个“百分占比的饼图”,自定义View的方式是继承自根View。

需要处理了View的绘制过程

《自定义View精炼详解第(四)课:onMeasure()解析和简单示例》里面实现的是示例是一个“世界九大艺术布局空间”,自定义View的方式是继承自现有布局(2)。

需要自己处理ViewGroup的测量和布局过程

《Android开发艺术探索》中对于自定义View的分类,用的分类方式就是上面的方式,非常的精彩,看了之后,茅塞顿开,原来笔者之前写的文章不知不觉中,已经实现了这4种自定义View的方式。自定义View最难的不是硬性的技能——View绘制原理”“View的层次结构”“事件分发机制”“View的弹性滑动”“滑动冲突”“这些,而是最难在“只见树木不见森林”。自定义View是很灵活的,开发者除了熟悉硬性技能之外,更要有一个全局的概念,前者随着开发的过程,会慢慢熟悉,但后者更多的需要经验总结。笔者的这套“自定义View的实战示例”课就是为自己做一个总结。

做为一个开始,本节课将和读者一起实现一个仿QQ空间页面顶部条随界面滑动背景透明度变化的效果,这个效果在其他应用程序中也很常见,技能+1。

二、上代码,具体实现

笔者之前的文章第二部分总是二话不说,直接上代码,很干脆,其实更好的方式是引导读者思考:这个效果如何实现。前期做好效果的功能分析,才能读者更好的理解。

QQ空间的这个页面其实并不复杂,我们看看QQ空间的演示界面:

自定义View实战第(一)课:仿QQ空间顶部条背景变化效果

可以看见,整个页面其实只有两个根元素,一个是ListView,一个是标题栏,前者可以上下滑动,给用户呈现内容;后者固定位置不动,类似于一个导航栏,左边一个返回键图标,中间一段文字,右边一个内容添加图标,与用户进行交互。那么我们要自定义的View是哪一个,明显是样式变化的ListView,因为它长的和普通的ListView不一样,“谁家的ListView顶个黑色矩形做头部”

①自定义View代码段——核心代码段

package com.example.administrator.myview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;

/**
 * Created by Administrator on 2018/4/3 0003.
 * 拿到ListView滚动事件
 * 拿到高度变化
 * 根据高度变化,设置顶部条的背景
 * powered by Cpf.com.
 */
public class ScrollChangeHeadView extends ListView {

    private View viewHead;
    private View topBar;

    public ScrollChangeHeadView(Context context,AttributeSet attrs) {
        super(context,attrs);

    viewHead = LayoutInflater.from(context).inflate(R.layout.scroll_change_head,null);
        addHeaderView(viewHead);//这个addHeaderView

    setOnScrollListener(new OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (topBar != null) {
                //滚动中
                int headTop = viewHead.getTop();
                headTop = Math.abs(headTop);

                //0-255 0是全透明 255是不透明
                topBar.getBackground().setAlpha(headTop);
            }
        }
    });
    }
    //人为的构造一个方法,用来传值,主类获取对象实例之后,通过自定义的这个方法把对象传入第二类,然后第二类才能做事
    public void setTopBar(View v){
        topBar = v;
    }
}
           

本段代码是核心代码段:我们在这段代码里做了最关键的三件事:

1)拿到在自定义View内部拿到ListView的滚动事件;

2)在滚动事件里面拿到矩形头部的高度变化;

3)根据矩形头部的高度变化,设置顶部条的背景;

在其中,还涉及了几个方法,这里简单讲解一下,帮助读者理解,1) addHeaderView(),这个方法是ListView中方法,作用就是Add a fixed view to appear at the top of the list.为ListView的顶部部分增加一个顶部矩形;2)getTop(),这个方法是View中的方法,作用就是Top position of this view relative to its parent.,作用就是获得一个View的高度,在滚动事件里调用这个方法,就可以不断得到View的高度数据,以便于当做参数值传入setAlpha()方法中。

②自定义View的XML布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#0f0c2f"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我是head"
            android:textColor="#fff" />

    </LinearLayout>

</LinearLayout>
           

这个布局文件里面,我们设置了添加进ListView的顶部部分的样式,也就是矩形的样式,便于显示,这里的颜色设置为何QQ空间的顶部背景色一样。

③主布局XML文件中引入自定义布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.administrator.myview.MainActivity">

    <com.example.administrator.myview.ScrollChangeHeadView
        android:id="@+id/schv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></com.example.administrator.myview.ScrollChangeHeadView>

    <LinearLayout
        android:id="@+id/topBar"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="#12b7f5"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="QQ空间"
            android:textColor="#fff"/>
    </LinearLayout>

</RelativeLayout>
           

主布局文件中引入了自定义的LlstView,然后再在后面布局了一个LinerLayout,作为顶部条的装载容器,放入一个TextView,用于显示顶部条文字。

④主Activity代码段

package com.example.administrator.myview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            LinearLayout topBar = (LinearLayout) findViewById(R.id.topBar);
            ScrollChangeHeadView schv = (ScrollChangeHeadView) findViewById(R.id.schv);

            schv.setTopBar(topBar);

            schv.setAdapter(new BaseAdapter() {
                @Override
                public int getCount() {
                    return 100;
                }

                @Override
                public Object getItem(int position) {
                    return null;
                }

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

                @Override
                public View getView(int position, View convertView, ViewGroup parent) {
                    TextView tv = new TextView(MainActivity.this);
                    tv.setText("+"+position);
                    return tv;
                }
            });
    }
}
           

主Activity中,我们获取到了两个控件,1)然后给自定义的ListView设置适配器和简单的传入数据;2)接着把顶部条的实例对象topBar获取到,作为参数传入到自定义控件里面去。

运行效果如下:(~(@^_^@)~)

自定义View实战第(一)课:仿QQ空间顶部条背景变化效果

小结:本节内容主要是实现了一个仿QQ空间顶部条随滚动事件发生而背景变化的效果,在应用程序中的使用率蛮高,还有一些其他的对于顶部条的处理,其实现方式其实都比较类似,比如下面这个“厨房故事”(2016年谷歌Material Design设计奖获得者)的应用程序(笔者用的是最新版本),其顶部条的变化,我们也可以运用上面的方法,进行实现,效果是顶部条随滚动事件的发生而出现或者隐藏。有兴趣的读者,可以参考笔者的代码进行实现。

自定义View实战第(一)课:仿QQ空间顶部条背景变化效果

继续阅读