提交 ad5ddd95 编写于 作者: singwhatiwanna's avatar singwhatiwanna

upload code

上级 790513db
The MIT License (MIT)
Copyright (c) 2014 singwhatiwanna
https://github.com/singwhatiwanna
http://blog.csdn.net/singwhatiwanna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PinnedHeaderExpandableListView</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ryg.expandable"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-19
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<gradient
android:angle="0"
android:centerColor="#CCCCCC"
android:endColor="#FFFFFF"
android:height="1px"
android:startColor="#FFFFFF" />
</shape>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/Grey" android:state_pressed="true"></item>
<item android:drawable="@color/Grey" android:state_selected="true"></item>
<item android:drawable="@color/LightGray"></item>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/LightGray" android:state_pressed="true"></item>
<item android:drawable="@color/LightGray" android:state_selected="true"></item>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/selector_item"
android:gravity="center_vertical"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants"
android:padding="5dp" >
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="@+id/age"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:id="@+id/address"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<ImageView
android:id="@+id/image"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/collapse"
android:visibility="gone" />
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Button" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:background="@drawable/selector_group">
<TextView
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="精品推荐" />
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/collapse"
android:visibility="gone" />
</RelativeLayout>
\ No newline at end of file
<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:groupIndicator="@null" 取消默认图片
android:childIndicatorLeft 设置孩子左边间距
android:dividerHeight 这个高度一定要设置,不然显示不出来分割线,估计默认为0 吧
android:childDivider="@drawable/child_bg" 这个直接引color,或者图片会导致整个孩子背景都为这个颜色
-->
<com.ryg.expandable.ui.StickyLayout
android:id="@+id/sticky_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:background="#78A524"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
</LinearLayout>
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.ryg.expandable.ui.PinnedHeaderExpandableListView
android:id="@+id/expandablelist"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="@null"
android:childDivider="@drawable/child_bg"
android:childIndicatorLeft="0dp"
android:divider="@color/Grey"
android:dividerHeight="1dp"
android:groupIndicator="@null"
android:scrollbarAlwaysDrawHorizontalTrack="false" >
</com.ryg.expandable.ui.PinnedHeaderExpandableListView>
</LinearLayout>
</com.ryg.expandable.ui.StickyLayout>
</RelativeLayout>
\ No newline at end of file
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_settings"
android:title="@string/menu_settings"
android:orderInCategory="100" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#ffffff</color>
<color name="lucensy">#00000001</color>
<color name="translucency">#e0000000</color>
<color name="orange">#ffa500</color>
<color name="blue">#0066dd</color>
<color name="black">#000000</color>
<!-- <color name="red">#bf242a</color> -->
<color name="transparent">#00000000</color>
<color name="light_orange">#fcb75c</color>
<color name="alpha_00">#00000000</color>
<color name="LightGray">#D3D3D3</color>
<color name="trans">#50000000</color>
<color name="red">#fe0202</color>
<color name="background">#e5e5e5</color>
<color name="transparent_background">#50000000</color>
<color name="Grey">#BEBEBE</color>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">可展开的列表</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">MainActivity</string>
</resources>
\ No newline at end of file
<resources>
<style name="AppTheme" parent="android:Theme.Light" />
</resources>
\ No newline at end of file
package com.ryg.expandable;
public class Group {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
package com.ryg.expandable;
import java.util.ArrayList;
import java.util.List;
import com.ryg.expandable.R;
import com.ryg.expandable.ui.PinnedHeaderExpandableListView;
import com.ryg.expandable.ui.StickyLayout;
import com.ryg.expandable.ui.PinnedHeaderExpandableListView.OnHeaderUpdateListener;
import com.ryg.expandable.ui.StickyLayout.OnGiveUpTouchEventListener;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.LayoutParams;
import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements
ExpandableListView.OnChildClickListener,
ExpandableListView.OnGroupClickListener,
OnHeaderUpdateListener, OnGiveUpTouchEventListener {
private PinnedHeaderExpandableListView expandableListView;
private StickyLayout stickyLayout;
private ArrayList<Group> groupList;
private ArrayList<List<People>> childList;
private MyexpandableListAdapter adapter;
private ViewGroup mHeaderView;
@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);
}
/***
* 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);
}
}
/***
* 数据源
*
* @author Administrator
*
*/
class MyexpandableListAdapter extends BaseExpandableListAdapter {
private Context context;
private LayoutInflater inflater;
public MyexpandableListAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}
// 返回父列表个数
@Override
public int getGroupCount() {
return groupList.size();
}
// 返回子列表个数
@Override
public int getChildrenCount(int groupPosition) {
return childList.get(groupPosition).size();
}
@Override
public Object getGroup(int groupPosition) {
return groupList.get(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return childList.get(groupPosition).get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
GroupHolder groupHolder = null;
if (convertView == null) {
groupHolder = new GroupHolder();
convertView = inflater.inflate(R.layout.group, null);
groupHolder.textView = (TextView) convertView
.findViewById(R.id.group);
groupHolder.imageView = (ImageView) convertView
.findViewById(R.id.image);
convertView.setTag(groupHolder);
} else {
groupHolder = (GroupHolder) convertView.getTag();
}
groupHolder.textView.setText(((Group) getGroup(groupPosition))
.getTitle());
if (isExpanded)// ture is Expanded or false is not isExpanded
groupHolder.imageView.setImageResource(R.drawable.expanded);
else
groupHolder.imageView.setImageResource(R.drawable.collapse);
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
ChildHolder childHolder = null;
if (convertView == null) {
childHolder = new ChildHolder();
convertView = inflater.inflate(R.layout.child, null);
childHolder.textName = (TextView) convertView
.findViewById(R.id.name);
childHolder.textAge = (TextView) convertView
.findViewById(R.id.age);
childHolder.textAddress = (TextView) convertView
.findViewById(R.id.address);
childHolder.imageView = (ImageView) convertView
.findViewById(R.id.image);
Button button = (Button) convertView
.findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "clicked pos=", Toast.LENGTH_SHORT).show();
}
});
convertView.setTag(childHolder);
} else {
childHolder = (ChildHolder) convertView.getTag();
}
childHolder.textName.setText(((People) getChild(groupPosition,
childPosition)).getName());
childHolder.textAge.setText(String.valueOf(((People) getChild(
groupPosition, childPosition)).getAge()));
childHolder.textAddress.setText(((People) getChild(groupPosition,
childPosition)).getAddress());
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
@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;
}
class GroupHolder {
TextView textView;
ImageView imageView;
}
class ChildHolder {
TextView textName;
TextView textAge;
TextView textAddress;
ImageView imageView;
}
@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());
}
@Override
public boolean giveUpTouchEvent(MotionEvent event) {
if (expandableListView.getFirstVisiblePosition() == 0) {
View view = expandableListView.getChildAt(0);
if (view != null && view.getTop() >= 0) {
return true;
}
}
return false;
}
}
package com.ryg.expandable;
public class People {
private String name;
private int age;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
/**
The MIT License (MIT)
Copyright (c) 2014 singwhatiwanna
https://github.com/singwhatiwanna
http://blog.csdn.net/singwhatiwanna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package com.ryg.expandable.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ExpandableListView;
import android.widget.AbsListView.OnScrollListener;
public class PinnedHeaderExpandableListView extends ExpandableListView implements OnScrollListener {
private static final String TAG = "PinnedHeaderExpandableListView";
public interface OnHeaderUpdateListener {
/**
* 采用单例模式返回同一个view对象即可
* 注意:view必须要有LayoutParams
*/
public View getPinnedHeader();
public void updatePinnedHeader(int firstVisibleGroupPos);
}
private View mHeaderView;
private int mHeaderWidth;
private int mHeaderHeight;
private OnScrollListener mScrollListener;
private OnHeaderUpdateListener mHeaderUpdateListener;
private boolean mActionDownHappened = false;
public PinnedHeaderExpandableListView(Context context) {
super(context);
initView();
}
public PinnedHeaderExpandableListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public PinnedHeaderExpandableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
setFadingEdgeLength(0);
setOnScrollListener(this);
}
@Override
public void setOnScrollListener(OnScrollListener l) {
if (l != this) {
mScrollListener = l;
}
super.setOnScrollListener(this);
}
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();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mHeaderView == null) {
return;
}
measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
mHeaderWidth = mHeaderView.getMeasuredWidth();
mHeaderHeight = mHeaderView.getMeasuredHeight();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mHeaderView == null) {
return;
}
mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mHeaderView != null) {
drawChild(canvas, mHeaderView, getDrawingTime());
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
Log.d(TAG, "dispatchTouchEvent");
int pos = pointToPosition(x, y);
if (y >= mHeaderView.getTop() && y <= mHeaderView.getBottom()) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mActionDownHappened = true;
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
int groupPosition = getPackedPositionGroup(getExpandableListPosition(pos));
if (groupPosition != INVALID_POSITION && mActionDownHappened) {
if (isGroupExpanded(groupPosition)) {
collapseGroup(groupPosition);
} else {
expandGroup(groupPosition);
}
mActionDownHappened = false;
}
}
return true;
}
return super.dispatchTouchEvent(ev);
}
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);
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mHeaderView != null && scrollState == SCROLL_STATE_IDLE) {
int firstVisiblePos = getFirstVisiblePosition();
if (firstVisiblePos == 0) {
mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
}
}
if (mScrollListener != null) {
mScrollListener.onScrollStateChanged(view, scrollState);
}
}
@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);
}
}
}
/**
The MIT License (MIT)
Copyright (c) 2014 singwhatiwanna
https://github.com/singwhatiwanna
http://blog.csdn.net/singwhatiwanna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package com.ryg.expandable.ui;
import java.util.NoSuchElementException;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
public class StickyLayout extends LinearLayout {
private static final String TAG = "StickyLayout";
public interface OnGiveUpTouchEventListener {
public boolean giveUpTouchEvent(MotionEvent event);
}
private View mHeader;
private View mContent;
private OnGiveUpTouchEventListener mGiveUpTouchEventListener;
// header的高度 单位:px
private int mOriginalHeaderHeight;
private int mHeaderHeight;
private int mStatus = STATUS_EXPANDED;
public static final int STATUS_EXPANDED = 1;
public static final int STATUS_COLLAPSED = 2;
private int mTouchSlop;
// 分别记录上次滑动的坐标
private int mLastX = 0;
private int mLastY = 0;
// 分别记录上次滑动的坐标(onInterceptTouchEvent)
private int mLastXIntercept = 0;
private int mLastYIntercept = 0;
// 用来控制滑动角度,仅当角度a满足如下条件才进行滑动:tan a = deltaX / deltaY > 2
private static final int TAN = 2;
public StickyLayout(Context context) {
super(context);
}
public StickyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public StickyLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus && (mHeader == null || mContent == null)) {
initData();
}
}
private void initData() {
int headerId= getResources().getIdentifier("header", "id", getContext().getPackageName());
int contentId = getResources().getIdentifier("content", "id", getContext().getPackageName());
if (headerId != 0 && contentId != 0) {
mHeader = findViewById(headerId);
mContent = findViewById(contentId);
mOriginalHeaderHeight = mHeader.getMeasuredHeight();
mHeaderHeight = mOriginalHeaderHeight;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
Log.d(TAG, "mTouchSlop = " + mTouchSlop);
} else {
throw new NoSuchElementException("Did your view with \"header\" or \"content\" exist?");
}
}
public void setOnGiveUpTouchEventListener(OnGiveUpTouchEventListener l) {
mGiveUpTouchEventListener = l;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int intercepted = 0;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastXIntercept = x;
mLastYIntercept = y;
mLastX = x;
mLastY = y;
intercepted = 0;
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if (mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop) {
intercepted = 1;
} else if (mGiveUpTouchEventListener != null) {
if (mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop) {
intercepted = 1;
}
}
break;
}
case MotionEvent.ACTION_UP: {
intercepted = 0;
mLastXIntercept = mLastYIntercept = 0;
break;
}
default:
break;
}
Log.d(TAG, "intercepted=" + intercepted);
return intercepted != 0;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
Log.d(TAG, "x=" + x + " y=" + y + " mlastY=" + mLastY);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
Log.d(TAG, "mHeaderHeight=" + mHeaderHeight + " deltaY=" + deltaY + " mlastY=" + mLastY);
mHeaderHeight += deltaY;
setHeaderHeight(mHeaderHeight);
break;
}
case MotionEvent.ACTION_UP: {
// 这里做了下判断,当松开手的时候,会自动向两边滑动,具体向哪边滑,要看当前所处的位置
int destHeight = 0;
if (mHeaderHeight <= mOriginalHeaderHeight * 0.5) {
destHeight = 0;
mStatus = STATUS_COLLAPSED;
} else {
destHeight = mOriginalHeaderHeight;
mStatus = STATUS_EXPANDED;
}
// 慢慢滑向终点
this.smoothSetHeaderHeight(mHeaderHeight, destHeight, 500);
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return true;
}
public void smoothSetHeaderHeight(final int from, final int to, long duration) {
final int frameCount = (int) (duration / 1000f * 30) + 1;
final float partation = (to - from) / (float) frameCount;
new Thread("Thread#smoothSetHeaderHeight") {
@Override
public void run() {
for (int i = 0; i < frameCount; i++) {
final int height;
if (i == frameCount - 1) {
height = to;
} else {
height = (int) (from + partation * i);
}
post(new Runnable() {
public void run() {
setHeaderHeight(height);
}
});
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
}
private void setHeaderHeight(int height) {
Log.d(TAG, "setHeaderHeight height=" + height);
if (height < 0) {
height = 0;
} else if (height > mOriginalHeaderHeight) {
height = mOriginalHeaderHeight;
}
if (mHeaderHeight != height || true) {
mHeaderHeight = height;
mHeader.getLayoutParams().height = mHeaderHeight;
mHeader.requestLayout();
}
}
}
package com.ryg.utils;
import android.content.Context;
import android.util.TypedValue;
public class Utils {
public static int dp2px(Context context, int dp) {
return Math.round(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册