├── .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 | slideback 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 |