Skip to content

Commit ea4c6a7

Browse files
committed
仿airbnb
1 parent d13d416 commit ea4c6a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1458
-299
lines changed

.idea/misc.xml

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/qaplug_profiles.xml

Lines changed: 289 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -4,169 +4,14 @@
44

55
[项目地址](https://github.com/CSnowStack/BehaviorDemo)
66

7-
[下载体验](https://github.com/CSnowStack/BehaviorDemo/blob/master/img/Behavior.apk)
87

98
#### 建议
109
请先阅读[这篇文章](http://www.jianshu.com/p/f7989a2a3ec2)
1110
> 好多东西抄自这里
1211
13-
#### 效果来源
14-
15-
[这里是地址](https://material.uplabs.com/posts/profile-4f03fc6b-1a82-42ab-8a3e-f50dcbc10253)
16-
17-
18-
![原图](https://github.com/CSnowStack/BehaviorDemo/blob/master/img/preview.gif)
1912

2013
#### 我的实现
2114

22-
![实现的效果](https://github.com/CSnowStack/BehaviorDemo/blob/master/img/c.gif)
23-
24-
25-
26-
27-
> 一些细节没有实现 ,见谅,录制的gif效果也不太好 :-(
28-
29-
30-
### 实现
31-
32-
#### 依赖关系
33-
`Tab` 监听 `onNestedPreScroll`来进行滑动,`Toolbar` 依赖 `FytContent`,其余的依赖 `Tab`
34-
35-
#### 变化
36-
- `Tab` 的移动是手指滑动距离的 1/2 ,会根据停下来的位置判断是应该回到原位置还是下一个状态并进行移动
37-
38-
- `VP` 跟随 `Tab`, `HeaderScrollingViewBehavior`什么的请看建议,移动则没什么难度
39-
40-
- `BGContent` 跟随 `Tab`,根据`Tab`运动的比例,缩放,移动,修改 里面`View``Alpha`
41-
42-
- `BG` 跟随 `Tab`,首先向下移动到 `BGContent` 的高度的 1/2的地方
43-
44-
- `Editor` 跟随 `Tab`,首先移动到 `BGContent`下面加上预留的`Padding`,随着比例移动并设置alpha
45-
46-
- `Icon` 跟随 `Tab`,根据`Tab`运动的比例进行移动和调整大小
47-
48-
- `Name` 同上
49-
50-
- `Socre` 同上,没有缩放
51-
52-
53-
- `ToolBarIcon` 跟随`BGContent` ,根据`BGContent`移动的比例修改图标的`Alpha`
54-
55-
#### 部分代码
56-
57-
```java
58-
//TabBehavior
59-
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
60-
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
61-
mUp = dy > 0;
62-
if(isChildRequestScroll(child.getTranslationY())){//如果list需要滑动这边就不动
63-
consumed[1]=0;
64-
return;
65-
}
66-
consumed[1]=dy;//全部消耗
67-
int distance = -dy / 2;//降低移动的速度
68-
69-
70-
if (child.getTranslationY() + distance < -mMaxDistance) {
71-
distance = -mMaxDistance;
72-
} else if (child.getTranslationY() + distance > 0) {
73-
distance = 0;
74-
} else {
75-
distance = (int) (child.getTranslationY() + distance);
76-
}
77-
child.setTranslationY(distance);
78-
}
79-
80-
/**
81-
* Child是否需要滑动
82-
*/
83-
private boolean isChildRequestScroll(float translationY) {
84-
return (translationY == -mMaxDistance &&//在顶部
85-
mViewPager.getAdapter() != null && //有适配器
86-
mViewPager.getAdapter().getCount() > 0 &&//有item
87-
mViewPager.getAdapter() instanceof IsChildRequestScrollListener && //实现了
88-
((IsChildRequestScrollListener) mViewPager.getAdapter()).requestScroll(mUp)//需要滑动
89-
);
90-
}
91-
92-
93-
//设置 listener 检测是否需要展开
94-
@Override
95-
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
96-
mControlChange=true;
97-
98-
if(mViewPager.getAdapter() != null && //有适配器
99-
mViewPager.getAdapter().getCount() > 0 &&//有item
100-
mViewPager.getAdapter() instanceof SupportNeedExpendListener&&
101-
((SupportNeedExpendListener) mViewPager.getAdapter()).getNeedExpendListener()==null){
102-
((SupportNeedExpendListener) mViewPager.getAdapter()).setNeedExpendListener(this);
103-
}
104-
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
105-
}
106-
107-
/**
108-
* list fling到头的时候 展开
109-
*/
110-
@Override
111-
public void needExpand() {
112-
if(!mControlChange){
113-
mValueAnimator.setDuration(500);
114-
mValueAnimator.removeAllUpdateListeners();
115-
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
116-
@Override
117-
public void onAnimationUpdate(ValueAnimator animation) {
118-
mTab.setTranslationY((animation.getAnimatedFraction()-1)*mMaxDistance);
119-
}
120-
});
121-
mValueAnimator.start();
122-
}
123-
}
124-
125-
/**
126-
* 开启硬件离屏缓存,放入不服导致缓存失效的 view
127-
* 效果都还好
128-
*/
129-
if(mHardwareViews.size()==0){
130-
mHardwareViews.add(parent.findViewById(R.id.txt_name));
131-
mHardwareViews.add(parent.findViewById(R.id.img_icon));
132-
mHardwareViews.add(parent.findViewById(R.id.lyt_score));
133-
mHardwareViews.add(parent.findViewById(R.id.tab_layout));
134-
mHardwareViews.add(parent.findViewById(R.id.bg));
135-
mHardwareViews.add(parent.findViewById(R.id.lyt_editor));
136-
mHardwareViews.add(parent.findViewById(R.id.lyt_statistics));
137-
138-
//开启硬件离屏缓存
139-
mValueAnimator.addListener(new AnimatorListenerAdapter() {
140-
@Override
141-
public void onAnimationEnd(Animator animation) {
142-
super.onAnimationEnd(animation);
143-
for(View v:mHardwareViews){
144-
v.setLayerType(View.LAYER_TYPE_NONE,null);
145-
}
146-
}
147-
148-
@Override
149-
public void onAnimationStart(Animator animation) {
150-
super.onAnimationStart(animation);
151-
for(View v:mHardwareViews){
152-
v.setLayerType(View.LAYER_TYPE_HARDWARE,null);
153-
}
154-
}
155-
});
156-
}
157-
158-
159-
```
160-
161-
>代码是蛮简单的 ,直接看项目即可,就那几行代码, 又加了点功能 耦合度变高了的感觉欢迎 Star,提 issue 还有PR
162-
163-
164-
##### 开启硬件离屏缓存
165-
![开启硬件离屏缓存](https://github.com/CSnowStack/BehaviorDemo/blob/master/img/open.gif)
166-
167-
##### 关闭硬件离屏缓存
168-
![关闭硬件离屏缓存](https://github.com/CSnowStack/BehaviorDemo/blob/master/img/close.gif)
15+
![实现的效果](https://github.com/CSnowStack/BehaviorDemo/blob/master/img/preview.gif.gif)
16916

17017

171-
### TODO
172-
- ~~在向上fling的过程中,向下滑,会出现错乱的情况 :-(~~

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ dependencies {
2626
compile "com.android.support:recyclerview-v7:$android_support_version"
2727
compile "com.android.support:cardview-v7:$android_support_version"
2828
compile 'de.hdodenhof:circleimageview:2.1.0'
29+
compile 'com.android.support.constraint:constraint-layout:1.0.2'
30+
2931
}

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
android:label="@string/app_name"
99
android:supportsRtl="true"
1010
android:theme="@style/AppTheme">
11-
<activity android:name=".MainActivity">
11+
<activity android:name="cq.airbnb.AirbnbActivity"
12+
android:hardwareAccelerated="true">
1213
<intent-filter>
1314
<action android:name="android.intent.action.MAIN"/>
1415

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cq.airbnb;
2+
3+
import android.os.Bundle;
4+
import android.support.design.widget.CoordinatorLayout;
5+
import android.support.design.widget.TabLayout;
6+
import android.support.v4.view.ViewPager;
7+
import android.support.v7.app.AppCompatActivity;
8+
import android.view.View;
9+
import android.widget.LinearLayout;
10+
11+
import cq.behaviordemo.R;
12+
import cq.behaviordemo.adapter.ItemAdapter;
13+
14+
/**
15+
* 仿爱彼迎主页
16+
*/
17+
18+
public class AirbnbActivity extends AppCompatActivity {
19+
private TabLayout mTabLayout;
20+
private ViewPager mViewPager;
21+
private LinearLayout mLytUP;
22+
private LinearLayout mLytAll;
23+
24+
@Override protected void onCreate(Bundle savedInstanceState) {
25+
super.onCreate(savedInstanceState);
26+
setContentView(R.layout.activity_airbnb);
27+
findView();
28+
initView();
29+
initEvent();
30+
mTabLayout.setTranslationY(getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_min));
31+
}
32+
33+
private void findView() {
34+
mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
35+
mViewPager = (ViewPager) findViewById(R.id.viewpager);
36+
mLytUP = (LinearLayout) findViewById(R.id.lyt_up);
37+
mLytAll = (LinearLayout) findViewById(R.id.lyt_all);
38+
39+
mLytUP.setOnClickListener(new View.OnClickListener() {
40+
@Override public void onClick(View v) {
41+
((TabBehavior) ((CoordinatorLayout.LayoutParams) mTabLayout.getLayoutParams()).getBehavior()).hideItem();
42+
43+
}
44+
});
45+
mLytAll.setOnClickListener(new View.OnClickListener() {
46+
@Override public void onClick(View v) {
47+
((TabBehavior) ((CoordinatorLayout.LayoutParams) mTabLayout.getLayoutParams()).getBehavior()).needExpand();
48+
49+
}
50+
});
51+
}
52+
53+
private void initView() {
54+
mViewPager.setAdapter(new ItemAdapter(getSupportFragmentManager(), mViewPager, 2));
55+
mTabLayout.setupWithViewPager(mViewPager);
56+
}
57+
58+
private void initEvent() {
59+
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
60+
@Override
61+
public void onTabSelected(TabLayout.Tab tab) {
62+
mViewPager.setCurrentItem(tab.getPosition());
63+
}
64+
65+
@Override
66+
public void onTabUnselected(TabLayout.Tab tab) {
67+
68+
}
69+
70+
@Override
71+
public void onTabReselected(TabLayout.Tab tab) {
72+
73+
}
74+
});
75+
76+
}
77+
78+
79+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package cq.airbnb;
2+
3+
import android.content.Context;
4+
import android.support.design.widget.CoordinatorLayout;
5+
import android.support.design.widget.TabLayout;
6+
import android.util.AttributeSet;
7+
import android.view.View;
8+
9+
import cq.behaviordemo.R;
10+
11+
/**
12+
* 背景的behavior
13+
*/
14+
15+
public class BGBehavior extends CoordinatorLayout.Behavior {
16+
17+
private int mTranslationMax;
18+
public BGBehavior(Context context, AttributeSet attrs) {
19+
super(context, attrs);
20+
mTranslationMax=context.getResources().getDimensionPixelOffset(R.dimen.airbnb_translation_max);
21+
}
22+
23+
24+
@Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
25+
return dependency instanceof TabLayout;
26+
}
27+
28+
@Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
29+
child.setTranslationY(dependency.getTranslationY()-mTranslationMax);
30+
31+
return true;
32+
}
33+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package cq.airbnb;
2+
3+
import android.content.Context;
4+
import android.support.design.widget.CoordinatorLayout;
5+
import android.support.design.widget.TabLayout;
6+
import android.util.AttributeSet;
7+
import android.view.View;
8+
9+
import java.util.List;
10+
11+
import cq.behaviordemo.R;
12+
import cq.behaviordemo.behavior.HeaderScrollingViewBehavior;
13+
14+
/**
15+
* 控制列表的移动
16+
*/
17+
18+
public class ListBehavior extends HeaderScrollingViewBehavior {
19+
private int mHeightToolbar;
20+
public ListBehavior(Context context, AttributeSet attrs) {
21+
super(context, attrs);
22+
mHeightToolbar=context.getResources().getDimensionPixelOffset(R.dimen.toolbar_height);
23+
}
24+
25+
26+
@Override
27+
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
28+
return isDependOn(dependency);
29+
}
30+
31+
32+
@Override public View findFirstDependency(List<View> views) {
33+
for (View view : views) {
34+
if (isDependOn(view))
35+
return view;
36+
}
37+
return null;
38+
}
39+
40+
@Override protected int getScrollRange(View v) {
41+
if (isDependOn(v)) {
42+
return 0;
43+
} else {
44+
return super.getScrollRange(v);
45+
}
46+
}
47+
48+
private boolean isDependOn(View dependency) {
49+
return dependency instanceof TabLayout;
50+
}
51+
52+
//跟随tab移动
53+
@Override
54+
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
55+
56+
child.setTranslationY(dependency.getTranslationY());
57+
return true;
58+
}
59+
}

0 commit comments

Comments
 (0)