├── .gitignore
├── LICENSE
├── README.md
├── SlideActivity
├── .gitignore
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── chenjishi
│ │ │ └── slidedemo
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── chenjishi
│ │ │ │ └── slidedemo
│ │ │ │ ├── DetailActivity.java
│ │ │ │ ├── ImageActivity.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── ViewPagerActivity.java
│ │ │ │ └── base
│ │ │ │ ├── Slide.java
│ │ │ │ ├── SlideActivity.java
│ │ │ │ └── SlideLayout.java
│ │ └── res
│ │ │ ├── anim
│ │ │ ├── close_enter.xml
│ │ │ ├── close_exit.xml
│ │ │ ├── open_enter.xml
│ │ │ └── open_exit.xml
│ │ │ ├── drawable-xhdpi
│ │ │ ├── alien.jpeg
│ │ │ └── ic_launcher.png
│ │ │ ├── drawable
│ │ │ ├── highlight_bkg.xml
│ │ │ └── sliding_back_shadow.xml
│ │ │ ├── layout
│ │ │ ├── activity_detail.xml
│ │ │ ├── activity_image.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_view_pager.xml
│ │ │ ├── item_layout.xml
│ │ │ └── slide_layout.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── chenjishi
│ │ └── slidedemo
│ │ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
└── demo.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Intellij project files
26 | *.iml
27 | *.ipr
28 | *.iws
29 | .idea/
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Jishi Chen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SlideActivity
2 | =============
3 |
4 | A iOS like slide back Activity framework, slide left edge to exit current activity.
5 |
6 | If you have any idea to improve this project, any commits welcomed:)
7 |
8 | download demo in Google Play [SlideActivity](https://play.google.com/store/apps/details?id=com.chenjishi.slidedemo&hl=zh-CN)
9 | ## ScreenShot
10 |
11 |
12 |
13 |
14 | ## Usage
15 |
16 | ### 1.Extend SlideActivity
17 | ```
18 | public class DetailActivity extends SlideActivity {
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.activity_detail);
24 | }
25 | }
26 | ```
27 |
28 | ### 2.Start Activity By Slide Method
29 | ```
30 | public void onButtonClicked(View v) {
31 | Intent intent = new Intent(this, ImageActivity.class);
32 | Slide.getInstance().startActivity(this, intent);
33 | }
34 | ```
35 |
36 | ### All Done!
37 |
38 |
39 |
--------------------------------------------------------------------------------
/SlideActivity/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/SlideActivity/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/SlideActivity/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 | defaultConfig {
7 | applicationId "com.chenjishi.slidedemo"
8 | minSdkVersion 17
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.3.1'
28 | compile 'com.android.support:support-compat:25.3.1'
29 | compile 'com.android.support:recyclerview-v7:25.3.1'
30 | testCompile 'junit:junit:4.12'
31 | }
32 |
--------------------------------------------------------------------------------
/SlideActivity/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/jishichen/android/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/androidTest/java/com/chenjishi/slidedemo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.chenjishi.slidedemo", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/java/com/chenjishi/slidedemo/DetailActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import com.chenjishi.slidedemo.base.Slide;
7 | import com.chenjishi.slidedemo.base.SlideActivity;
8 |
9 | /**
10 | * Created by chenjishi on 14-3-17.
11 | */
12 | public class DetailActivity extends SlideActivity {
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_detail);
18 | }
19 |
20 | public void onButtonClicked(View v) {
21 | Intent intent = new Intent(this, ImageActivity.class);
22 | Slide.getInstance().startActivity(this, intent);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/java/com/chenjishi/slidedemo/ImageActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo;
2 |
3 | import android.os.Bundle;
4 | import com.chenjishi.slidedemo.base.SlideActivity;
5 |
6 | /**
7 | * Created by chenjishi on 15/10/13.
8 | */
9 | public class ImageActivity extends SlideActivity {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.activity_image);
15 | }
16 | }
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/java/com/chenjishi/slidedemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.os.Bundle;
7 | import android.support.v4.app.FragmentActivity;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.TextView;
14 | import com.chenjishi.slidedemo.base.Slide;
15 |
16 | import static android.support.v7.widget.RecyclerView.Adapter;
17 | import static android.support.v7.widget.RecyclerView.ViewHolder;
18 |
19 | public class MainActivity extends FragmentActivity implements View.OnClickListener {
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_main);
25 |
26 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
27 | recyclerView.setLayoutManager(new LinearLayoutManager(this));
28 | recyclerView.setAdapter(new SimpleAdapter());
29 | final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
30 | paint.setStyle(Paint.Style.FILL);
31 | paint.setColor(0xFFD8D8D8);
32 | recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
33 | @Override
34 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
35 | int left = parent.getPaddingLeft();
36 | int right = parent.getWidth() - parent.getPaddingRight();
37 |
38 | int childCount = parent.getChildCount();
39 | for (int i = 0; i < childCount; i++) {
40 | View child = parent.getChildAt(i);
41 |
42 | RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
43 | int top = child.getBottom() + params.bottomMargin;
44 | int bottom = top + 1;
45 |
46 | c.drawRect(left, top, right, bottom, paint);
47 | }
48 | }
49 | });
50 |
51 | findViewById(R.id.btn_view_pager).setOnClickListener(this);
52 | }
53 |
54 | @Override
55 | public void onClick(View v) {
56 | Slide.getInstance().startActivity(this, new Intent(this, ViewPagerActivity.class));
57 | }
58 |
59 | @Override
60 | protected void onDestroy() {
61 | Slide.getInstance().clear();
62 | super.onDestroy();
63 | }
64 |
65 | private class SimpleAdapter extends Adapter {
66 |
67 | @Override
68 | public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
69 | View v = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout,
70 | parent, false);
71 | return new ItemViewHolder(v);
72 | }
73 |
74 | @Override
75 | public void onBindViewHolder(ItemViewHolder holder, int position) {
76 | holder.textView.setText("slide activity item index at " + position);
77 | holder.itemView.setOnClickListener(mOnClickListener);
78 | }
79 |
80 | @Override
81 | public int getItemCount() {
82 | return 20;
83 | }
84 | }
85 |
86 | private static class ItemViewHolder extends ViewHolder {
87 |
88 | public TextView textView;
89 |
90 | public ItemViewHolder(View itemView) {
91 | super(itemView);
92 | textView = (TextView) itemView.findViewById(R.id.title_label);
93 | }
94 | }
95 |
96 | private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
97 | @Override
98 | public void onClick(View v) {
99 | Intent intent = new Intent(MainActivity.this, DetailActivity.class);
100 | Slide.getInstance().startActivity(MainActivity.this, intent);
101 | }
102 | };
103 | }
104 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/java/com/chenjishi/slidedemo/ViewPagerActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.view.PagerAdapter;
6 | import android.support.v4.view.ViewPager;
7 | import android.util.TypedValue;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.TextView;
11 | import com.chenjishi.slidedemo.base.SlideActivity;
12 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
13 |
14 | /**
15 | * Created by jishichen on 2017/6/22.
16 | */
17 | public class ViewPagerActivity extends SlideActivity {
18 |
19 | @Override
20 | protected void onCreate(@Nullable Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_view_pager);
23 |
24 | ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
25 | viewPager.setOffscreenPageLimit(3);
26 | viewPager.setAdapter(new SimplePagerAdapter());
27 | setScrollableViewId(R.id.view_pager);
28 | }
29 |
30 | private class SimplePagerAdapter extends PagerAdapter {
31 |
32 | private float density;
33 |
34 | public SimplePagerAdapter() {
35 | density = getResources().getDisplayMetrics().density;
36 | }
37 |
38 | @Override
39 | public Object instantiateItem(ViewGroup container, int position) {
40 | ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT);
41 | TextView v = new TextView(ViewPagerActivity.this);
42 | v.setTextColor(0xFF333333);
43 | v.setText(R.string.description);
44 | v.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
45 | v.setLineSpacing(4 * density, 1);
46 | int padding = (int) (density * 8 + .5);
47 | v.setPadding(padding, padding, padding, padding);
48 | container.addView(v, lp);
49 | return v;
50 | }
51 |
52 | @Override
53 | public int getCount() {
54 | return 4;
55 | }
56 |
57 | @Override
58 | public boolean isViewFromObject(View view, Object object) {
59 | return view == object;
60 | }
61 |
62 | @Override
63 | public void destroyItem(ViewGroup container, int position, Object object) {
64 | container.removeView((View) object);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/java/com/chenjishi/slidedemo/base/Slide.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo.base;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import java.util.Stack;
8 |
9 | /**
10 | * Created by jishichen on 2017/6/16.
11 | */
12 | public class Slide {
13 |
14 | private static final Slide INSTANCE = new Slide();
15 |
16 | private Stack stack;
17 |
18 | private Slide() {
19 | stack = new Stack<>();
20 | }
21 |
22 | public void clear() {
23 | stack.clear();
24 | }
25 |
26 | public static Slide getInstance() {
27 | return INSTANCE;
28 | }
29 |
30 | public Activity peek() {
31 | if (stack.empty()) return null;
32 | return stack.peek();
33 | }
34 |
35 | public void pop() {
36 | if (stack.empty()) return;
37 |
38 | stack.pop();
39 | }
40 |
41 | public void startActivity(final Context context, Intent intent) {
42 | stack.push((Activity) context);
43 | context.startActivity(intent);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/java/com/chenjishi/slidedemo/base/SlideActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo.base;
2 |
3 | import android.app.Activity;
4 | import android.app.ActivityOptions;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Canvas;
7 | import android.os.Build;
8 | import android.support.v4.app.FragmentActivity;
9 | import android.support.v4.view.ViewPager;
10 | import android.util.DisplayMetrics;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.FrameLayout;
15 | import android.widget.ImageView;
16 | import com.chenjishi.slidedemo.R;
17 |
18 | import java.lang.reflect.InvocationHandler;
19 | import java.lang.reflect.Method;
20 | import java.lang.reflect.Proxy;
21 |
22 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23 |
24 | /**
25 | * Created by chenjishi on 14-3-17.
26 | */
27 | public class SlideActivity extends FragmentActivity implements SlideLayout.SlideListener {
28 |
29 | private SlideLayout mSlideLayout;
30 |
31 | private View mPreview;
32 |
33 | private float mInitOffset;
34 |
35 | @Override
36 | public void setContentView(int layoutResID) {
37 | super.setContentView(R.layout.slide_layout);
38 |
39 | mSlideLayout = (SlideLayout) findViewById(R.id.slide_layout);
40 |
41 | DisplayMetrics metrics = getResources().getDisplayMetrics();
42 | mInitOffset = -(1f / 3) * metrics.widthPixels;
43 |
44 | View contentView = LayoutInflater.from(this).inflate(layoutResID, null);
45 | mSlideLayout.addView(contentView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
46 | mSlideLayout.setShadowResource(R.drawable.sliding_back_shadow);
47 | mSlideLayout.setSlidingListener(this);
48 | mSlideLayout.setEdgeSize((int) (metrics.density * 20));
49 | }
50 |
51 | //handle conflict with view pager
52 | protected void setScrollableViewId(int id) {
53 | if (null == mSlideLayout) return;
54 |
55 | View v = findViewById(id);
56 | if (null == v || !(v instanceof ViewPager)) return;
57 | mSlideLayout.setViewPager((ViewPager) v);
58 | }
59 |
60 | @Override
61 | public void onViewCaptured() {
62 | mSlideLayout.setCanSlide(false);
63 | Activity activity = Slide.getInstance().peek();
64 | if (null == activity) return;
65 |
66 | View v = activity.findViewById(android.R.id.content);
67 | if (null == v) return;
68 |
69 | if (v.getWidth() == 0 || v.getHeight() == 0) return;
70 |
71 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
72 | FrameLayout rootView = (FrameLayout) findViewById(android.R.id.content);
73 | if (null == mPreview) {
74 | mPreview = new ImageView(this);
75 | rootView.addView(mPreview, 0, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
76 | }
77 |
78 | Bitmap bmp = null;
79 | try {
80 | bmp = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
81 | Bitmap.Config.ARGB_8888);
82 | } catch (OutOfMemoryError e) {
83 | bmp = null;
84 | }
85 |
86 | if (null != bmp) {
87 | v.draw(new Canvas(bmp));
88 | ((ImageView) mPreview).setImageBitmap(bmp);
89 | mSlideLayout.setCanSlide(true);
90 | }
91 | } else {
92 | mPreview = v;
93 | convertActivityToTranslucent();
94 | }
95 | }
96 |
97 | @Override
98 | public void onPanelSlide(View panel, float slideOffset) {
99 | if (slideOffset <= 0) {
100 | mPreview.setTranslationX(0);
101 | } else if (slideOffset < 1) {
102 | mPreview.setTranslationX(mInitOffset * (1 - slideOffset));
103 | } else {
104 | mPreview.setTranslationX(0);
105 | finish();
106 | overridePendingTransition(0, 0);
107 | }
108 | }
109 |
110 | @Override
111 | protected void onDestroy() {
112 | Slide.getInstance().pop();
113 | super.onDestroy();
114 | }
115 |
116 | private void convertActivityToTranslucent() {
117 | try {
118 | Method getActivityOptions = Activity.class.getDeclaredMethod("getActivityOptions");
119 | getActivityOptions.setAccessible(true);
120 | Object options = getActivityOptions.invoke(this);
121 |
122 | Class>[] classes = Activity.class.getDeclaredClasses();
123 | Class> listener = null;
124 | for (Class clazz : classes) {
125 | if (clazz.getSimpleName().contains("TranslucentConversionListener")) {
126 | listener = clazz;
127 | }
128 | }
129 | if (null == listener) return;
130 |
131 | Class>[] classArray = new Class[1];
132 | classArray[0] = listener;
133 | Object translucentListener = Proxy.newProxyInstance(listener.getClassLoader(),
134 | classArray, new TranslucentChangeListener());
135 |
136 | Method method = Activity.class.getDeclaredMethod("convertToTranslucent",
137 | listener, ActivityOptions.class);
138 | method.setAccessible(true);
139 | method.invoke(this, translucentListener, options);
140 | } catch (Throwable t) {
141 | }
142 | }
143 |
144 | private class TranslucentChangeListener implements InvocationHandler {
145 | @Override
146 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
147 | if (method.getName().equalsIgnoreCase("onTranslucentConversionComplete")
148 | && null != args && args.length > 0) {
149 | Object drawComplete = args[0];
150 | if (drawComplete instanceof Boolean) {
151 | boolean canSlide = (Boolean) drawComplete;
152 | mSlideLayout.setCanSlide(canSlide);
153 | }
154 | }
155 |
156 | return null;
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/java/com/chenjishi/slidedemo/base/SlideLayout.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo.base;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.graphics.drawable.Drawable;
9 | import android.os.Build;
10 | import android.support.v4.view.AccessibilityDelegateCompat;
11 | import android.support.v4.view.MotionEventCompat;
12 | import android.support.v4.view.ViewCompat;
13 | import android.support.v4.view.ViewPager;
14 | import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
15 | import android.support.v4.widget.ViewDragHelper;
16 | import android.util.AttributeSet;
17 | import android.view.MotionEvent;
18 | import android.view.View;
19 | import android.view.ViewGroup;
20 | import android.view.ViewParent;
21 | import android.view.accessibility.AccessibilityEvent;
22 |
23 | import java.lang.reflect.Field;
24 | import java.lang.reflect.Method;
25 | import java.util.ArrayList;
26 |
27 | public class SlideLayout extends ViewGroup {
28 |
29 | private static final int MIN_FLING_VELOCITY = 400; // dips per second
30 |
31 | private Drawable mShadowDrawable;
32 |
33 | private boolean mCanSlide;
34 |
35 | private View mSlideableView;
36 |
37 | private float mSlideOffset;
38 |
39 | private int mSlideRange;
40 |
41 | private boolean mIsUnableToDrag;
42 |
43 | private float mInitialMotionX;
44 | private float mInitialMotionY;
45 |
46 | private float mEdgeSize;
47 |
48 | private ViewPager mViewPager;
49 |
50 | private SlideListener mSlideListener;
51 |
52 | private final ViewDragHelper mDragHelper;
53 |
54 | private boolean mPreservedOpenState;
55 | private boolean mFirstLayout = true;
56 |
57 | private final ArrayList mPostedRunnables =
58 | new ArrayList();
59 |
60 | static final SlidingPanelLayoutImpl IMPL;
61 |
62 | static {
63 | final int deviceVersion = Build.VERSION.SDK_INT;
64 | if (deviceVersion >= 17) {
65 | IMPL = new SlidingPanelLayoutImplJBMR1();
66 | } else if (deviceVersion >= 16) {
67 | IMPL = new SlidingPanelLayoutImplJB();
68 | } else {
69 | IMPL = new SlidingPanelLayoutImplBase();
70 | }
71 | }
72 |
73 | public interface SlideListener {
74 |
75 | void onPanelSlide(View panel, float slideOffset);
76 |
77 | void onViewCaptured();
78 | }
79 |
80 | public SlideLayout(Context context) {
81 | this(context, null);
82 | }
83 |
84 | public SlideLayout(Context context, AttributeSet attrs) {
85 | this(context, attrs, 0);
86 | }
87 |
88 | public SlideLayout(Context context, AttributeSet attrs, int defStyle) {
89 | super(context, attrs, defStyle);
90 |
91 | final float density = context.getResources().getDisplayMetrics().density;
92 |
93 | setWillNotDraw(false);
94 |
95 | ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
96 | ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
97 |
98 | mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
99 | mDragHelper.setMinVelocity(MIN_FLING_VELOCITY * density);
100 | }
101 |
102 | public void setCanSlide(boolean b) {
103 | mCanSlide = b;
104 | }
105 |
106 | public void setSlidingListener(SlideListener listener) {
107 | mSlideListener = listener;
108 | }
109 |
110 | void dispatchOnPanelSlide(View panel) {
111 | if (mSlideListener != null) {
112 | mSlideListener.onPanelSlide(panel, mSlideOffset);
113 | }
114 | }
115 |
116 | public void setEdgeSize(int offset) {
117 | mEdgeSize = offset;
118 | }
119 |
120 | @Override
121 | protected void onAttachedToWindow() {
122 | super.onAttachedToWindow();
123 | mFirstLayout = true;
124 | }
125 |
126 | @Override
127 | protected void onDetachedFromWindow() {
128 | super.onDetachedFromWindow();
129 | mFirstLayout = true;
130 |
131 | for (int i = 0, count = mPostedRunnables.size(); i < count; i++) {
132 | final DisableLayerRunnable dlr = mPostedRunnables.get(i);
133 | dlr.run();
134 | }
135 | mPostedRunnables.clear();
136 | }
137 |
138 | @Override
139 | public void addView(View child) {
140 | if (getChildCount() > 0) {
141 | throw new IllegalStateException("SlideLayout can host only one direct child");
142 | }
143 | super.addView(child);
144 | }
145 |
146 | @Override
147 | public void addView(View child, int index) {
148 | if (getChildCount() > 0) {
149 | throw new IllegalStateException("SlideLayout can host only one direct child");
150 | }
151 | super.addView(child, index);
152 | }
153 |
154 | @Override
155 | public void addView(View child, int width, int height) {
156 | if (getChildCount() > 0) {
157 | throw new IllegalStateException("SlideLayout can host only one direct child");
158 | }
159 | super.addView(child, width, height);
160 | }
161 |
162 | @Override
163 | public void addView(View child, ViewGroup.LayoutParams params) {
164 | if (getChildCount() > 0) {
165 | throw new IllegalStateException("SlideLayout can host only one direct child");
166 | }
167 | super.addView(child, params);
168 | }
169 |
170 | @Override
171 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
172 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
173 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
174 | if (widthMode != MeasureSpec.EXACTLY) {
175 | throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
176 | }
177 |
178 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
179 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
180 | if (heightMode != MeasureSpec.EXACTLY) {
181 | throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
182 | }
183 |
184 | final int childCount = getChildCount();
185 |
186 | // We'll find the current one below.
187 | mSlideableView = null;
188 |
189 | for (int i = 0; i < childCount; i++) {
190 | final View child = getChildAt(i);
191 | final LayoutParams lp = (LayoutParams) child.getLayoutParams();
192 |
193 | int childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
194 | int childHeightSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
195 |
196 | child.measure(childWidthSpec, childHeightSpec);
197 | lp.slideable = true;
198 | mSlideableView = child;
199 | }
200 |
201 | setMeasuredDimension(widthSize, heightSize);
202 | mCanSlide = true;
203 |
204 | if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {
205 | // Cancel scrolling in progress, it's no longer relevant.
206 | mDragHelper.abort();
207 | }
208 | }
209 |
210 | @Override
211 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
212 | mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
213 |
214 | final int width = r - l;
215 | final int paddingStart = getPaddingLeft();
216 | final int paddingEnd = getPaddingRight();
217 | final int paddingTop = getPaddingTop();
218 |
219 | final int childCount = getChildCount();
220 | int xStart = paddingStart;
221 | int nextXStart = xStart;
222 |
223 | if (mFirstLayout) {
224 | mSlideOffset = mCanSlide && mPreservedOpenState ? 1.f : 0.f;
225 | }
226 |
227 | for (int i = 0; i < childCount; i++) {
228 | final View child = getChildAt(i);
229 |
230 | final LayoutParams lp = (LayoutParams) child.getLayoutParams();
231 |
232 | final int childWidth = child.getMeasuredWidth();
233 | int offset = 0;
234 |
235 | if (lp.slideable) {
236 | final int range = childWidth;
237 | mSlideRange = range;
238 | final int lpMargin = lp.leftMargin;
239 | final int pos = (int) (range * mSlideOffset);
240 | xStart += pos + lpMargin;
241 | mSlideOffset = (float) pos / mSlideRange;
242 | } else {
243 | xStart = nextXStart;
244 | }
245 |
246 | final int childLeft = xStart - offset;
247 | final int childRight = childLeft + childWidth;
248 |
249 | final int childTop = paddingTop;
250 | final int childBottom = childTop + child.getMeasuredHeight();
251 | child.layout(childLeft, paddingTop, childRight, childBottom);
252 |
253 | nextXStart += child.getWidth();
254 | }
255 |
256 | mFirstLayout = false;
257 | }
258 |
259 | @Override
260 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
261 | super.onSizeChanged(w, h, oldw, oldh);
262 | // Recalculate sliding panes and their details
263 | if (w != oldw) {
264 | mFirstLayout = true;
265 | }
266 | }
267 |
268 | @Override
269 | public void requestChildFocus(View child, View focused) {
270 | super.requestChildFocus(child, focused);
271 | if (!isInTouchMode() && !mCanSlide) {
272 | mPreservedOpenState = child == mSlideableView;
273 | }
274 | }
275 |
276 | @Override
277 | public boolean onInterceptTouchEvent(MotionEvent ev) {
278 | final int action = MotionEventCompat.getActionMasked(ev);
279 |
280 | if (!mCanSlide || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
281 | mDragHelper.cancel();
282 | return super.onInterceptTouchEvent(ev);
283 | }
284 |
285 | if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
286 | mDragHelper.cancel();
287 | return false;
288 | }
289 |
290 | boolean interceptTap = false;
291 |
292 | switch (action) {
293 | case MotionEvent.ACTION_DOWN: {
294 | mIsUnableToDrag = false;
295 | final float x = ev.getX();
296 | final float y = ev.getY();
297 | mInitialMotionX = x;
298 | mInitialMotionY = y;
299 |
300 | if (x > mEdgeSize) {
301 | mDragHelper.cancel();
302 | mIsUnableToDrag = true;
303 | return false;
304 | }
305 |
306 | break;
307 | }
308 |
309 | case MotionEvent.ACTION_MOVE: {
310 | final float x = ev.getX();
311 | final float y = ev.getY();
312 | final float adx = Math.abs(x - mInitialMotionX);
313 | final float ady = Math.abs(y - mInitialMotionY);
314 | final int slop = mDragHelper.getTouchSlop();
315 | if (adx > slop && ady > adx) {
316 | mDragHelper.cancel();
317 | mIsUnableToDrag = true;
318 | return false;
319 | }
320 |
321 | if (null != mViewPager && mInitialMotionX > mEdgeSize) {
322 | mDragHelper.cancel();
323 | mIsUnableToDrag = true;
324 | return false;
325 | }
326 |
327 | if (null != mViewPager && canViewPagerScroll(mViewPager, (int) adx)) {
328 | mDragHelper.cancel();
329 | mIsUnableToDrag = true;
330 | return false;
331 | }
332 | }
333 | }
334 |
335 | final boolean interceptForDrag = mDragHelper.shouldInterceptTouchEvent(ev);
336 |
337 | return interceptForDrag || interceptTap;
338 | }
339 |
340 | @Override
341 | public boolean onTouchEvent(MotionEvent ev) {
342 | if (!mCanSlide) return super.onTouchEvent(ev);
343 |
344 | mDragHelper.processTouchEvent(ev);
345 |
346 | final int action = ev.getAction();
347 | boolean wantTouchEvents = true;
348 |
349 | switch (action & MotionEventCompat.ACTION_MASK) {
350 | case MotionEvent.ACTION_DOWN: {
351 | final float x = ev.getX();
352 | final float y = ev.getY();
353 | mInitialMotionX = x;
354 | mInitialMotionY = y;
355 | break;
356 | }
357 |
358 | case MotionEvent.ACTION_UP: {
359 | break;
360 | }
361 | }
362 |
363 | return wantTouchEvents;
364 | }
365 |
366 | public void setViewPager(ViewPager viewPager) {
367 | mViewPager = viewPager;
368 | }
369 |
370 | private boolean canViewPagerScroll(ViewPager p, int dx) {
371 | if (dx == 0) return false;
372 |
373 | final int index = p.getCurrentItem();
374 | return !(dx > 0 && index <= 0 || dx < 0
375 | && index >= p.getAdapter().getCount() - 1);
376 | }
377 |
378 | private void onPanelDragged(int newLeft) {
379 | if (mSlideableView == null) {
380 | // This can happen if we're aborting motion during layout because everything now fits.
381 | mSlideOffset = 0;
382 | return;
383 | }
384 | final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
385 |
386 | final int paddingStart = getPaddingLeft();
387 | final int lpMargin = lp.leftMargin;
388 | final int startBound = paddingStart + lpMargin;
389 |
390 | mSlideOffset = (float) (newLeft - startBound) / mSlideRange;
391 |
392 | dispatchOnPanelSlide(mSlideableView);
393 | }
394 |
395 | @Override
396 | protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
397 | boolean result;
398 | final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);
399 |
400 | if (Build.VERSION.SDK_INT >= 11) {
401 | result = super.drawChild(canvas, child, drawingTime);
402 | } else {
403 | if (child.isDrawingCacheEnabled()) {
404 | child.setDrawingCacheEnabled(false);
405 | }
406 | result = super.drawChild(canvas, child, drawingTime);
407 | }
408 |
409 | canvas.restoreToCount(save);
410 |
411 | return result;
412 | }
413 |
414 | private void invalidateChildRegion(View v) {
415 | IMPL.invalidateChildRegion(this, v);
416 | }
417 |
418 | @Override
419 | public void computeScroll() {
420 | if (mDragHelper.continueSettling(true)) {
421 | if (!mCanSlide) {
422 | mDragHelper.abort();
423 | return;
424 | }
425 |
426 | ViewCompat.postInvalidateOnAnimation(this);
427 | }
428 | }
429 |
430 | public void setShadowResource(int resId) {
431 | mShadowDrawable = getResources().getDrawable(resId);
432 | }
433 |
434 | @Override
435 | public void draw(Canvas c) {
436 | super.draw(c);
437 |
438 | final View shadowView = mSlideableView;
439 | if (shadowView == null || mShadowDrawable == null) {
440 | // No need to draw a shadow if we don't have one.
441 | return;
442 | }
443 |
444 | final int top = shadowView.getTop();
445 | final int bottom = shadowView.getBottom();
446 |
447 | final int shadowWidth = mShadowDrawable.getIntrinsicWidth();
448 | final int right = shadowView.getLeft();
449 | final int left = right - shadowWidth;
450 |
451 | mShadowDrawable.setBounds(left, top, right, bottom);
452 | mShadowDrawable.draw(c);
453 | }
454 |
455 | @Override
456 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
457 | return new LayoutParams();
458 | }
459 |
460 | @Override
461 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
462 | return p instanceof MarginLayoutParams
463 | ? new LayoutParams((MarginLayoutParams) p)
464 | : new LayoutParams(p);
465 | }
466 |
467 | @Override
468 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
469 | return p instanceof LayoutParams && super.checkLayoutParams(p);
470 | }
471 |
472 | @Override
473 | public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
474 | return new LayoutParams(getContext(), attrs);
475 | }
476 |
477 | private class DragHelperCallback extends ViewDragHelper.Callback {
478 |
479 | @Override
480 | public boolean tryCaptureView(View child, int pointerId) {
481 | if (mIsUnableToDrag) return false;
482 | return ((LayoutParams) child.getLayoutParams()).slideable;
483 | }
484 |
485 | @Override
486 | public void onViewDragStateChanged(int state) {
487 | if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
488 | if (mSlideOffset == 0) {
489 | mPreservedOpenState = false;
490 | } else {
491 | mPreservedOpenState = true;
492 | }
493 | }
494 | }
495 |
496 | @Override
497 | public void onViewCaptured(View capturedChild, int activePointerId) {
498 | if (null != mSlideListener) mSlideListener.onViewCaptured();
499 | }
500 |
501 | @Override
502 | public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
503 | onPanelDragged(left);
504 | invalidate();
505 | }
506 |
507 | @Override
508 | public void onViewReleased(View releasedChild, float xvel, float yvel) {
509 | final LayoutParams lp = (LayoutParams) releasedChild.getLayoutParams();
510 |
511 | int left = getPaddingLeft() + lp.leftMargin;
512 | if (xvel > 0 || (xvel == 0 && mSlideOffset > 0.5f)) {
513 | left += mSlideRange;
514 | }
515 | mDragHelper.settleCapturedViewAt(left, releasedChild.getTop());
516 | invalidate();
517 | }
518 |
519 | @Override
520 | public int getViewHorizontalDragRange(View child) {
521 | return mSlideRange;
522 | }
523 |
524 | @Override
525 | public int clampViewPositionHorizontal(View child, int left, int dx) {
526 | final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
527 |
528 | int startBound = getPaddingLeft() + lp.leftMargin;
529 | int endBound = startBound + mSlideRange;
530 | final int newLeft = Math.min(Math.max(left, startBound), endBound);
531 | return newLeft;
532 | }
533 |
534 | @Override
535 | public int clampViewPositionVertical(View child, int top, int dy) {
536 | // Make sure we never move views vertically.
537 | // This could happen if the child has less height than its parent.
538 | return child.getTop();
539 | }
540 |
541 | @Override
542 | public void onEdgeDragStarted(int edgeFlags, int pointerId) {
543 | mDragHelper.captureChildView(mSlideableView, pointerId);
544 | }
545 | }
546 |
547 | public static class LayoutParams extends MarginLayoutParams {
548 | private static final int[] ATTRS = new int[]{
549 | android.R.attr.layout_weight
550 | };
551 |
552 | /**
553 | * The weighted proportion of how much of the leftover space
554 | * this child should consume after measurement.
555 | */
556 | public float weight = 0;
557 |
558 | /**
559 | * True if this pane is the slideable pane in the layout.
560 | */
561 | boolean slideable;
562 |
563 | Paint dimPaint;
564 |
565 | public LayoutParams() {
566 | super(FILL_PARENT, FILL_PARENT);
567 | }
568 |
569 | public LayoutParams(int width, int height) {
570 | super(width, height);
571 | }
572 |
573 | public LayoutParams(ViewGroup.LayoutParams source) {
574 | super(source);
575 | }
576 |
577 | public LayoutParams(MarginLayoutParams source) {
578 | super(source);
579 | }
580 |
581 | public LayoutParams(LayoutParams source) {
582 | super(source);
583 | this.weight = source.weight;
584 | }
585 |
586 | public LayoutParams(Context c, AttributeSet attrs) {
587 | super(c, attrs);
588 |
589 | final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
590 | this.weight = a.getFloat(0, 0);
591 | a.recycle();
592 | }
593 |
594 | }
595 |
596 | interface SlidingPanelLayoutImpl {
597 | void invalidateChildRegion(SlideLayout parent, View child);
598 | }
599 |
600 | static class SlidingPanelLayoutImplBase implements SlidingPanelLayoutImpl {
601 | public void invalidateChildRegion(SlideLayout parent, View child) {
602 | ViewCompat.postInvalidateOnAnimation(parent, child.getLeft(), child.getTop(),
603 | child.getRight(), child.getBottom());
604 | }
605 | }
606 |
607 | static class SlidingPanelLayoutImplJB extends SlidingPanelLayoutImplBase {
608 | /*
609 | * Private API hacks! Nasty! Bad!
610 | *
611 | * In Jellybean, some optimizations in the hardware UI renderer
612 | * prevent a changed Paint on a View using a hardware layer from having
613 | * the intended effect. This twiddles some internal bits on the view to force
614 | * it to recreate the display list.
615 | */
616 | private Method mGetDisplayList;
617 | private Field mRecreateDisplayList;
618 |
619 | SlidingPanelLayoutImplJB() {
620 | try {
621 | mGetDisplayList = View.class.getDeclaredMethod("getDisplayList", (Class[]) null);
622 | } catch (NoSuchMethodException e) {
623 | }
624 | try {
625 | mRecreateDisplayList = View.class.getDeclaredField("mRecreateDisplayList");
626 | mRecreateDisplayList.setAccessible(true);
627 | } catch (NoSuchFieldException e) {
628 | }
629 | }
630 |
631 | @Override
632 | public void invalidateChildRegion(SlideLayout parent, View child) {
633 | if (mGetDisplayList != null && mRecreateDisplayList != null) {
634 | try {
635 | mRecreateDisplayList.setBoolean(child, true);
636 | mGetDisplayList.invoke(child, (Object[]) null);
637 | } catch (Exception e) {
638 | }
639 | } else {
640 | // Slow path. REALLY slow path. Let's hope we don't get here.
641 | child.invalidate();
642 | return;
643 | }
644 | super.invalidateChildRegion(parent, child);
645 | }
646 | }
647 |
648 | static class SlidingPanelLayoutImplJBMR1 extends SlidingPanelLayoutImplBase {
649 | @Override
650 | public void invalidateChildRegion(SlideLayout parent, View child) {
651 | ViewCompat.setLayerPaint(child, ((LayoutParams) child.getLayoutParams()).dimPaint);
652 | }
653 | }
654 |
655 | class AccessibilityDelegate extends AccessibilityDelegateCompat {
656 | private final Rect mTmpRect = new Rect();
657 |
658 | @Override
659 | public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
660 | final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info);
661 | super.onInitializeAccessibilityNodeInfo(host, superNode);
662 | copyNodeInfoNoChildren(info, superNode);
663 | superNode.recycle();
664 |
665 | info.setClassName(SlideLayout.class.getName());
666 | info.setSource(host);
667 |
668 | final ViewParent parent = ViewCompat.getParentForAccessibility(host);
669 | if (parent instanceof View) {
670 | info.setParent((View) parent);
671 | }
672 |
673 | // This is a best-approximation of addChildrenForAccessibility()
674 | // that accounts for filtering.
675 | final int childCount = getChildCount();
676 | for (int i = 0; i < childCount; i++) {
677 | final View child = getChildAt(i);
678 | if (!filter(child) && (child.getVisibility() == View.VISIBLE)) {
679 | // Force importance to "yes" since we can't read the value.
680 | ViewCompat.setImportantForAccessibility(
681 | child, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
682 | info.addChild(child);
683 | }
684 | }
685 | }
686 |
687 | @Override
688 | public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
689 | super.onInitializeAccessibilityEvent(host, event);
690 |
691 | event.setClassName(SlideLayout.class.getName());
692 | }
693 |
694 | @Override
695 | public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
696 | AccessibilityEvent event) {
697 | if (!filter(child)) {
698 | return super.onRequestSendAccessibilityEvent(host, child, event);
699 | }
700 | return false;
701 | }
702 |
703 | public boolean filter(View child) {
704 | return false;
705 | }
706 |
707 | /**
708 | * This should really be in AccessibilityNodeInfoCompat, but there unfortunately
709 | * seem to be a few elements that are not easily cloneable using the underlying API.
710 | * Leave it private here as it's not general-purpose useful.
711 | */
712 | private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest,
713 | AccessibilityNodeInfoCompat src) {
714 | final Rect rect = mTmpRect;
715 |
716 | src.getBoundsInParent(rect);
717 | dest.setBoundsInParent(rect);
718 |
719 | src.getBoundsInScreen(rect);
720 | dest.setBoundsInScreen(rect);
721 |
722 | dest.setVisibleToUser(src.isVisibleToUser());
723 | dest.setPackageName(src.getPackageName());
724 | dest.setClassName(src.getClassName());
725 | dest.setContentDescription(src.getContentDescription());
726 |
727 | dest.setEnabled(src.isEnabled());
728 | dest.setClickable(src.isClickable());
729 | dest.setFocusable(src.isFocusable());
730 | dest.setFocused(src.isFocused());
731 | dest.setAccessibilityFocused(src.isAccessibilityFocused());
732 | dest.setSelected(src.isSelected());
733 | dest.setLongClickable(src.isLongClickable());
734 |
735 | dest.addAction(src.getActions());
736 |
737 | dest.setMovementGranularities(src.getMovementGranularities());
738 | }
739 | }
740 |
741 | private class DisableLayerRunnable implements Runnable {
742 | final View mChildView;
743 |
744 | DisableLayerRunnable(View childView) {
745 | mChildView = childView;
746 | }
747 |
748 | @Override
749 | public void run() {
750 | if (mChildView.getParent() == SlideLayout.this) {
751 | ViewCompat.setLayerType(mChildView, ViewCompat.LAYER_TYPE_NONE, null);
752 | invalidateChildRegion(mChildView);
753 | }
754 | mPostedRunnables.remove(this);
755 | }
756 | }
757 | }
758 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/anim/close_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/anim/close_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/anim/open_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/anim/open_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/drawable-xhdpi/alien.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/app/src/main/res/drawable-xhdpi/alien.jpeg
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/drawable/highlight_bkg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/drawable/sliding_back_shadow.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/layout/activity_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
12 |
20 |
21 |
30 |
41 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/layout/activity_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
19 |
20 |
24 |
28 |
29 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
20 |
33 |
34 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/layout/activity_view_pager.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
19 |
20 |
24 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/layout/item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/layout/slide_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #9c0
4 | #303F9F
5 | #FF4081
6 | #66CCCCCC
7 |
8 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SlideActivity
3 | Alien is a 1979 British-American science-fiction horror
4 | film directed by Ridley Scott, and starring Sigourney Weaver, Tom Skerritt, Veronica
5 | Cartwright, Harry Dean Stanton, John Hurt, Ian Holm and Yaphet Kotto. The film\'s title
6 | refers to a highly aggressive extraterrestrial creature that stalks and attacks the crew
7 | of a spaceship. Dan O\'Bannon wrote the screenplay from a story he wrote with Ronald Shusett,
8 | drawing influence from previous works of science fiction and horror.
9 |
10 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/SlideActivity/app/src/test/java/com/chenjishi/slidedemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.chenjishi.slidedemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/SlideActivity/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/SlideActivity/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/SlideActivity/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/SlideActivity/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/SlideActivity/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/SlideActivity/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/SlideActivity/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/SlideActivity/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjishi/SlideActivity/060ebc02125dc9aad23a94f7d4071c186f6717c3/demo.gif
--------------------------------------------------------------------------------