Android自己定义组件系列【5】——进阶实践(2)

上一篇《Android自己定义组件系列【5】——进阶实践(1)》中对任老师的《可下拉的PinnedHeaderExpandableListView的实现》前一部分进行了实现,这一篇我们来看看ExpandableListView的使用并实现剩下的部分。

原文出处:http://blog.csdn.net/singwhatiwanna/article/details/25546871

一、ExpandableListView的使用方法

ExpandableListView是ListView的子类,它在普通ListView的基础上进行了扩展,适配器为ExpandableListAdapter。

与Adapter相似的是。实现ExpandableListAdapter也有例如以下方式:

1、扩展BaseExpandableListAdapter实现ExpandableListAdapter

2、使用SimpleExpandableListAdapter将两个List集合包装成ExpandableListAdapter

3、使用SimpleCursorTreeAdapter将Cursor中的数据包装成SimpleCursorTreeAdapter

接下来用第一种方式来做个小样例。来看看ExpandableListView的使用

		ExpandableListAdapter adapter = new BaseExpandableListAdapter() {

			@Override
			public boolean isChildSelectable(int arg0, int arg1) {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public boolean hasStableIds() {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public View getGroupView(int arg0, boolean arg1, View arg2, ViewGroup arg3) {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public long getGroupId(int arg0) {
				// TODO Auto-generated method stub
				return 0;
			}

			@Override
			public int getGroupCount() {
				// TODO Auto-generated method stub
				return 0;
			}

			@Override
			public Object getGroup(int arg0) {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public int getChildrenCount(int arg0) {
				// TODO Auto-generated method stub
				return 0;
			}

			@Override
			public View getChildView(int arg0, int arg1, boolean arg2, View arg3,
					ViewGroup arg4) {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public long getChildId(int arg0, int arg1) {
				// TODO Auto-generated method stub
				return 0;
			}

			@Override
			public Object getChild(int arg0, int arg1) {
				// TODO Auto-generated method stub
				return null;
			}
		};

能够看到BaseExpandableListApdater中的方法非常多,主要方法介绍例如以下:

getGroupCount():返回组列表数量

getGroupView():返回的View作为组列表项

getChildrenCount():返回子列表项的数量

getChildView():返回的View作为特定组、特定位置的子列表项

package com.example.expandablelistviewtest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		ExpandableListAdapter adapter = new BaseExpandableListAdapter() {

			int[] logos = new int[] {
				R.drawable.ic_launcher,
				R.drawable.ic_launcher,
				R.drawable.ic_launcher
			};

			private String[] groupTypes = new String[]{
				"计算机语言", "人类语言", "动物语言"
			};

			private String[][] childTypes = new String[][] {
					{"Java", "C++", "C", "PHP"},
					{"汉语", "英语", "日语", "法语"},
					{"咕咕", "汪汪", "喵喵"}
			};

			// 获取指定组位置、指定子列表项处的子列表项数据
			@Override
			public Object getChild(int groupPosition, int childPosition) {
				return childTypes[groupPosition][childPosition];
			}

			@Override
			public long getChildId(int groupPosition, int childPosition) {
				return childPosition;
			}

			@Override
			public int getChildrenCount(int groupPosition) {
				return childTypes[groupPosition].length;
			}

			private TextView getTextView() {
				AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
						ViewGroup.LayoutParams.MATCH_PARENT, 64);
				TextView textView = new TextView(MainActivity.this);
				textView.setLayoutParams(lp);
				textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
				textView.setPadding(36, 0, 0, 0);
				textView.setTextSize(20);
				return textView;
			}

			// 该方法决定每一个子选项的外观
			@Override
			public View getChildView(int groupPosition, int childPosition,
					boolean isLastChild, View convertView, ViewGroup parent) {
				TextView textView = getTextView();
				textView.setText(getChild(groupPosition, childPosition)
						.toString());
				return textView;
			}

			// 获取指定组位置处的组数据
			@Override
			public Object getGroup(int groupPosition) {
				return groupTypes[groupPosition];
			}

			@Override
			public int getGroupCount() {
				return groupTypes.length;
			}

			@Override
			public long getGroupId(int groupPosition) {
				return groupPosition;
			}

			// 该方法决定每一个组选项的外观
			@Override
			public View getGroupView(int groupPosition, boolean isExpanded,
					View convertView, ViewGroup parent) {
				LinearLayout ll = new LinearLayout(MainActivity.this);
				ll.setOrientation(0);
				ImageView logo = new ImageView(MainActivity.this);
				logo.setImageResource(logos[groupPosition]);
				ll.addView(logo);
				TextView textView = getTextView();
				textView.setText(getGroup(groupPosition).toString());
				ll.addView(textView);
				return ll;
			}

			@Override
			public boolean isChildSelectable(int groupPosition,
					int childPosition) {
				return true;
			}

			@Override
			public boolean hasStableIds() {
				return true;
			}
		};
		ExpandableListView expandListView = (ExpandableListView) findViewById(R.id.list);
		expandListView.setAdapter(adapter);
	}

}
<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=".MainActivity" >
	<ExpandableListView
	    android:id="@+id/list"
	    android:layout_width="match_parent"
	    android:layout_height="wrap_content"/>

</RelativeLayout>

二、代码分析

首先看onCreate方法:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        expandableListView = (PinnedHeaderExpandableListView) findViewById(R.id.expandablelist);
        stickyLayout = (StickyLayout)findViewById(R.id.sticky_layout);
        initData();

        adapter = new MyexpandableListAdapter(this);
        expandableListView.setAdapter(adapter);

        // 展开全部group
        for (int i = 0, count = expandableListView.getCount(); i < count; i++) {
            expandableListView.expandGroup(i);
        }

        expandableListView.setOnHeaderUpdateListener(this);
        expandableListView.setOnChildClickListener(this);
        expandableListView.setOnGroupClickListener(this);
        stickyLayout.setOnGiveUpTouchEventListener(this);

    }

前面几行非常easy。和上面的样例差点儿一样。我们仅仅须要再关注一下initData()方法和以下的几行监听函数。

initData()中是模拟的数据。例如以下:

    void initData() {
        groupList = new ArrayList<Group>();
        Group group = null;
        for (int i = 0; i < 3; i++) {
            group = new Group();
            group.setTitle("group-" + i);
            groupList.add(group);
        }

        childList = new ArrayList<List<People>>();
        for (int i = 0; i < groupList.size(); i++) {
            ArrayList<People> childTemp;
            if (i == 0) {
                childTemp = new ArrayList<People>();
                for (int j = 0; j < 13; j++) {
                    People people = new People();
                    people.setName("yy-" + j);
                    people.setAge(30);
                    people.setAddress("sh-" + j);

                    childTemp.add(people);
                }
            } else if (i == 1) {
                childTemp = new ArrayList<People>();
                for (int j = 0; j < 8; j++) {
                    People people = new People();
                    people.setName("ff-" + j);
                    people.setAge(40);
                    people.setAddress("sh-" + j);

                    childTemp.add(people);
                }
            } else {
                childTemp = new ArrayList<People>();
                for (int j = 0; j < 23; j++) {
                    People people = new People();
                    people.setName("hh-" + j);
                    people.setAge(20);
                    people.setAddress("sh-" + j);

                    childTemp.add(people);
                }
            }
            childList.add(childTemp);
        }

    }

接下来我们看看监听这几个监听函数

public class MainActivity extends Activity implements
        ExpandableListView.OnChildClickListener,
        ExpandableListView.OnGroupClickListener,
        OnHeaderUpdateListener, OnGiveUpTouchEventListener {

从Activity的继承关系上看。OnChildClickListener和OnGroupClickListener是ExpandableListView类的监听函数。

    @Override
    public boolean onGroupClick(final ExpandableListView parent, final View v,
            int groupPosition, final long id) {

        return false;
    }

    @Override
    public boolean onChildClick(ExpandableListView parent, View v,
            int groupPosition, int childPosition, long id) {
        Toast.makeText(MainActivity.this,
                childList.get(groupPosition).get(childPosition).getName(), 1)
                .show();

        return false;
    }

再来看看OnHeaderUpdateListener,这个监听函数实际上是在重写(自己定义)的ExpandableListView中自己定义的监听。

    public void setOnHeaderUpdateListener(OnHeaderUpdateListener listener) {
        mHeaderUpdateListener = listener;
        if (listener == null) {
            return;
        }
        mHeaderView = listener.getPinnedHeader();
        int firstVisiblePos = getFirstVisiblePosition();
        int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos));
        listener.updatePinnedHeader(firstVisibleGroupPos);
        requestLayout();
        postInvalidate();
    }

getPinnedHeader()方法创建(得到)一个列表头。然后通过updatePinnedHeader方法设置当前可见的子列表元素的组名称。

requestLayout()方法的作用是当view确定自身已经不再适合现有的区域时,该view本身调用这种方法要求parent view又一次调用他的onMeasure onLayout来对又一次设置自己位置。特别的当view的layoutparameter发生改变,而且它的值还没能应用到view上,这时候适合调用这种方法。

postInvalidate()方法的作用是实现界面刷新。

   public interface OnHeaderUpdateListener {
        /**
         * 採用单例模式返回同一个view对象就可以
         * 注意:view必须要有LayoutParams
         */
        public View getPinnedHeader();

        public void updatePinnedHeader(int firstVisibleGroupPos);
    }

从OnHeaderUpdateListener监听函数的定义上看。当触发监听后会调用两个方法的实现例如以下:

    @Override
    public View getPinnedHeader() {
        if (mHeaderView == null) {
            mHeaderView = (ViewGroup) getLayoutInflater().inflate(R.layout.group, null);
            mHeaderView.setLayoutParams(new LayoutParams(
                    LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        }
        return mHeaderView;
    }

    @Override
    public void updatePinnedHeader(int firstVisibleGroupPos) {
        Group firstVisibleGroup = (Group) adapter.getGroup(firstVisibleGroupPos);
        TextView textView = (TextView) getPinnedHeader().findViewById(R.id.group);
        textView.setText(firstVisibleGroup.getTitle());
    }

接下来我们须要知道什么情况下回触发这个监听函数。

   protected void refreshHeader() {
        if (mHeaderView == null) {
            return;
        }
        int firstVisiblePos = getFirstVisiblePosition();
        int pos = firstVisiblePos + 1;
        int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos));
        int group = getPackedPositionGroup(getExpandableListPosition(pos));

        if (group == firstVisibleGroupPos + 1) {
            View view = getChildAt(1);
            if (view.getTop() <= mHeaderHeight) {
                int delta = mHeaderHeight - view.getTop();
                mHeaderView.layout(0, -delta, mHeaderWidth, mHeaderHeight - delta);
            }
        } else {
            mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
        }

        if (mHeaderUpdateListener != null) {
            mHeaderUpdateListener.updatePinnedHeader(firstVisibleGroupPos);
        }
    }

能够看到再调用refreshHeader()方法的时候会触发updatePinnerHeader方法。

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        if (totalItemCount > 0) {
            refreshHeader();
        }
        if (mScrollListener != null) {
            mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
    }

呵呵,这下最终恍然大悟了。在onScroll方法中调用了refreshHeader,这就是说在滑动屏幕的时候OnHeaderUpdateListener监听会触发。会不断的推断是否应该改变列名称。

快凌晨1点钟了,今天就分析到这吧。明天继续。

再次声明一下,本文是为了学习Android自己定义组件,对任老师博客《可下拉的PinnedHeaderExpandableListView的实现》进行具体解读。假设有问题希望指出。

原文出处:http://blog.csdn.net/singwhatiwanna/article/details/25546871

时间: 2024-10-20 17:58:58

Android自己定义组件系列【5】——进阶实践(2)的相关文章

Android自己定义组件系列【6】——进阶实践(3)

上一篇<Android自己定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计划中间插一段"知识点",对Android中的事件分发机制进行解析.细心的朋友可能会发现,打开大牛写的Android项目,里面非常多组件都是自己定义的(这就是为什么界面和体验这么吸引你的原因),可是要灵活的去自己定义组件就必须对手势(也就是各种监听)必须熟悉,能处理好事件之间的关系. 先看一

Android自己定义组件系列【4】——自己定义ViewGroup实现双側滑动

在上一篇文章<Android自己定义组件系列[3]--自己定义ViewGroup实现側滑>中实现了仿Facebook和人人网的側滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布局示意图: 2.核心代码 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth

Android自己定义组件系列【3】——自己定义ViewGroup实现側滑

有关自己定义ViewGroup的文章已经非常多了,我为什么写这篇文章,对于刚開始学习的人或者对自己定义组件比較生疏的朋友尽管能够拿来主义的用了,可是要一步一步的实现和了解当中的过程和原理才干真真脱离别人的代码,举一反三却不easy,非常多博主事实上不愿意一步一步的去写,这样非常耗时,可是假设能对读者有帮助,能从这篇文章中学会自己定义组件就达到我的目的了. 第一步:搭建框架来实现一个3/5和2/5分屏的界面,效果例如以下: 最外层是一个自己定义的ViewGroup布局文件例如以下: package

Android自己定义组件系列【5】——高级实践(1)

在接下来的几篇文章将任老师的博文<您可以下拉PinnedHeaderExpandableListView实现>骤来具体实现.来学习一下大神的代码并记录一下. 原文出处:http://blog.csdn.net/singwhatiwanna/article/details/25546871 先看一下终于效果: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2luZ3doYXRpd2FubmE=/font/5a6L5L2T/fontsize/400/f

Android自己定义组件系列【1】——自己定义View及ViewGroup

View类是ViewGroup的父类,ViewGroup具有View的全部特性.ViewGroup主要用来充当View的容器.将当中的View作为自己孩子,并对其进行管理.当然孩子也能够是ViewGroup类型. View类一般用于画图操作,重写它的onDraw方法,但它不能够包括其它组件,没有addView(View view)方法. ViewGroup是一个组件容器,它能够包括不论什么组件,但必须重写onLayout(boolean changed,int l,int t,int r,int

Android自己定义组件系列【8】——面膜文字动画

我们掩盖文字动画Flash中非经货共同体共同,由于Android应用程序开发人员做你想要做这个动画在应用程序中去?本文中,我们看的是如何自己的定义ImageView来实现让一张文字图片实现文字的遮罩闪烁效果,以下先来看看效果吧. (录屏幕延时导致效果看起来不是非常好) 一.实现原理 实现原理是重写View的onCreate方法.获取图片资源后对每一个像素的透明度进行改动来实现,再启动一个线程来循环改变某个区域中的像素透明度. RGBA基础知识:(以下几段介绍文字引用自维基百科) RGBA是代表R

Android自己定义组件系列【2】——Scroller类

在上一篇中介绍了View类的scrollTo和scrollBy两个方法,对这两个方法不太了解的朋友能够先看<自己定义View及ViewGroup> scrollTo和scrollBy尽管实现了视图的偏移,可是却没有更好的控制移动过程,移动是瞬间进行的.Scroller类就是为解决问题而设计的. 打开Scroller的源码,能够看到startScroll方法: /** * Start scrolling by providing a starting point and the distance

Android自定义组件系列【7】——进阶实践(4)

上一篇<>中补充了关于Android中事件分发的过程知识,这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpandableListView的实现>. 一.StickyLayout中的OnGiveUpTouchEventListener接口的作用是什么? public interface OnGiveUpTouchEventListener { public boolean giveUpTouchEvent(MotionEvent event); } 在Sticky

Android自定义组件系列【5】——进阶实践(1)

简介 项目开发中发现问题.解决问题这个过程中会出现很多问题,比如重复出现.某个问题的遗留,这些问题的本质就是设计模式.今天记录设计模式的知识点. 内容 在java以及其他的面向对象设计模式中,类与类之间主要有6种关系,他们分别是:依赖.关联.聚合.组合.继承.实现.它们的耦合度依次增强. 依赖关系:对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系.关联关系:分为单向关联和双向关联.在java中,单向关联表现为:类A当中使用了