├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── misc.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── martinrgb │ │ │ └── waterfalllayout │ │ │ ├── recyclerview │ │ │ ├── Adapter.java │ │ │ ├── Card.java │ │ │ ├── ListActivity.java │ │ │ ├── SmoothScrollStaggerGridLayoutManager.java │ │ │ └── TouchRecyclerView.java │ │ │ └── utils │ │ │ ├── AnimUtil.java │ │ │ ├── AppBlockCanaryContext.java │ │ │ ├── BitmapUtil.java │ │ │ ├── DensityUtil.java │ │ │ ├── LogUtil.java │ │ │ ├── MultiGestureDetector.java │ │ │ ├── RoundedCornerLayout.java │ │ │ ├── ToastUtil.java │ │ │ ├── VibratorUtil.java │ │ │ └── WaterfallApplication.java │ │ └── res │ │ ├── anim │ │ ├── scale_down.xml │ │ ├── scale_up.xml │ │ ├── slide_down.xml │ │ ├── slide_in_right.xml │ │ ├── slide_out_right.xml │ │ └── slide_up.xml │ │ ├── drawable-nodpi │ │ ├── as0.jpg │ │ ├── as1.jpg │ │ ├── as2.jpg │ │ ├── as3.jpg │ │ ├── as4.jpg │ │ ├── as5.jpg │ │ ├── background.jpg │ │ ├── closebtn.png │ │ ├── closebtnp.png │ │ ├── icon0.png │ │ ├── icon1.png │ │ ├── icon2.png │ │ ├── icon3.png │ │ ├── icon4.png │ │ ├── icon5.png │ │ ├── iconlist.png │ │ ├── image0.png │ │ ├── image0_1.png │ │ ├── image1.png │ │ ├── image2.png │ │ ├── image3.png │ │ ├── image4.png │ │ ├── image5.png │ │ ├── longpressmenu.png │ │ ├── longpresstext.png │ │ ├── shadow.png │ │ ├── shadowlayer.png │ │ ├── statsdark.png │ │ ├── statshome.png │ │ ├── statswhite.png │ │ └── tinttext.png │ │ ├── drawable │ │ ├── icon.png │ │ ├── roundedge.xml │ │ └── roundedgebig.xml │ │ ├── layout │ │ ├── activity_list.xml │ │ ├── backup │ │ │ ├── activity_logo.xml │ │ │ ├── backup │ │ │ │ ├── LogoActivity.java │ │ │ │ └── MediaActivity.java │ │ │ └── media_layout.xml │ │ └── list_item.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── version.properties ├── art └── art_real.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── liboverscroll ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── me │ │ └── everything │ │ └── android │ │ └── ui │ │ └── overscroll │ │ ├── AnimUtil.java │ │ ├── HorizontalOverScrollBounceEffectDecorator.java │ │ ├── IOverScrollDecor.java │ │ ├── IOverScrollState.java │ │ ├── IOverScrollStateListener.java │ │ ├── IOverScrollUpdateListener.java │ │ ├── ListenerStubs.java │ │ ├── OverScrollBounceEffectDecoratorBase.java │ │ ├── OverScrollDecoratorHelper.java │ │ ├── VerticalOverScrollBounceEffectDecorator.java │ │ └── adapters │ │ ├── AbsListViewOverScrollDecorAdapter.java │ │ ├── HorizontalScrollViewOverScrollDecorAdapter.java │ │ ├── IOverScrollDecoratorAdapter.java │ │ ├── RecyclerViewOverScrollDecorAdapter.java │ │ ├── ScrollViewOverScrollDecorAdapter.java │ │ ├── StaticOverScrollDecorAdapter.java │ │ └── ViewPagerOverScrollDecorAdapter.java │ └── test │ └── java │ └── me │ └── everything │ └── android │ └── ui │ └── overscroll │ ├── HorizontalOverScrollBounceEffectDecoratorTest.java │ └── VerticalOverScrollBounceEffectDecoratorTest.java ├── recyclerviewhelper ├── .gitignore ├── .settings │ └── org.eclipse.buildship.core.prefs ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── github │ │ └── nisrulz │ │ └── recyclerviewhelper │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── github │ │ └── nisrulz │ │ └── recyclerviewhelper │ │ ├── RVHAdapter.java │ │ ├── RVHItemClickListener.java │ │ ├── RVHItemDividerDecoration.java │ │ ├── RVHItemTouchHelperCallback.java │ │ └── RVHViewHolder.java │ └── test │ └── java │ └── github │ └── nisrulz │ └── recyclerviewhelper │ └── ExampleUnitTest.java ├── settings.gradle ├── springylib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── zach │ │ └── salman │ │ └── springylib │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── zach │ │ │ └── salman │ │ │ └── springylib │ │ │ ├── SpringAnimationType.java │ │ │ ├── SpringyAnimator.java │ │ │ ├── SpringyListener.java │ │ │ └── springyRecyclerView │ │ │ ├── SpringyAdapterAnimationType.java │ │ │ └── SpringyAdapterAnimator.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── zach │ └── salman │ └── springylib │ └── ExampleUnitTest.java └── supportrenderscriptblur ├── .gitignore ├── build.gradle └── src └── main ├── AndroidManifest.xml └── java └── com └── eightbitlab └── supportrenderscriptblur └── SupportRenderScriptBlur.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # IntelliJ 37 | *.iml 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/gradle.xml 41 | .idea/assetWizardSettings.xml 42 | .idea/dictionaries 43 | .idea/libraries 44 | .idea/caches 45 | # Android Studio 3 in .gitignore file. 46 | .idea/caches/build_file_checksums.ser 47 | .idea/modules.xml 48 | 49 | # Keystore files 50 | # Uncomment the following lines if you do not want to check your keystore files in. 51 | #*.jks 52 | #*.keystore 53 | 54 | # External native build folder generated in Android Studio 2.2 and later 55 | .externalNativeBuild 56 | 57 | # Google Services (e.g. APIs or Firebase) 58 | # google-services.json 59 | 60 | # Freeline 61 | freeline.py 62 | freeline/ 63 | freeline_project_description.json 64 | 65 | # fastlane 66 | fastlane/report.xml 67 | fastlane/Preview.html 68 | fastlane/screenshots 69 | fastlane/test_output 70 | fastlane/readme.md 71 | 72 | # Version control 73 | vcs.xml 74 | 75 | # lint 76 | lint/intermediates/ 77 | lint/generated/ 78 | lint/outputs/ 79 | lint/tmp/ 80 | # lint/reports/ -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MI_Style_Multitask_Prototype 2 | An prototype simply explained how to make Xiaomi Style Multitask Interaction. 3 | 4 | **THIS IS NOT A LIBRARY,ONLY A DEMO,The Best Resolution to Preview in Emulator is 1080 x 2340 & xxhdpi** 5 | 6 | **THE WAY I WROTE LAYOUT IS SUCH A BAD EXAMPLE,I THINK THE ANIMATION & GESTURE PART MAYBE USEFUL FOR SOMEONE'S PROJECT** 7 | 8 | ![gif](https://github.com/MartinRGB/MIUIStyle_Multitask_Prototype/blob/master/art/art_real.gif?raw=true) 9 | 10 | - Multi gesture interaction 11 | - Use rebound engine for driving complex interaction & animation 12 | - Use Renderscript for simple blur effect. 13 | 14 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '27.0.3' 6 | defaultConfig { 7 | applicationId "com.martinrgb.waterfalllayout" 8 | minSdkVersion 21 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "0.1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | renderscriptTargetApi 25 //must match target sdk and build tools, 23+ 14 | renderscriptSupportModeEnabled true 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | 23 | //如果version.properties文件可读则执行操作 24 | // android.applicationVariants.all { variant -> 25 | // variant.outputs.all { output -> 26 | // def outputFile = output.outputFile 27 | // if (outputFile != null && outputFile.name.endsWith('.apk')) { 28 | // //这里修改apk文件名 29 | //// def fileName = outputFile.name.replace("app","${defaultConfig.applicationId }_${defaultConfig.versionName}_${releaseTime() }") 30 | // def fileName = outputFile.name.replace("app","${getAppName()}" + "_${defaultConfig.versionName}") 31 | // outputFileName = new File(outputFile.parent, fileName) 32 | // } 33 | // } 34 | // } 35 | } 36 | 37 | dependencies { 38 | implementation project(':springylib') 39 | implementation project(':liboverscroll') 40 | implementation project(':supportrenderscriptblur') 41 | implementation project(':recyclerviewhelper') 42 | 43 | implementation fileTree(include: ['*.jar'], dir: 'libs') 44 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 45 | exclude group: 'com.android.support', module: 'support-annotations' 46 | }) 47 | implementation 'com.android.support:appcompat-v7:25.1.0' 48 | implementation 'com.android.support:recyclerview-v7:25.1.0' 49 | implementation 'com.android.support:cardview-v7:25.1.0' 50 | implementation 'com.android.support:design:26.+' 51 | implementation 'com.eightbitlab:blurview:1.3.3' 52 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' 53 | releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' 54 | implementation 'com.github.markzhai:blockcanary-android:1.5.0' 55 | implementation 'com.jakewharton:butterknife:8.8.1' 56 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 57 | testImplementation 'junit:junit:4.12' 58 | implementation 'com.facebook.rebound:rebound:0.3.8' 59 | // compile 'me.everything:overscroll-decor-android:1.0.4' 60 | } 61 | -------------------------------------------------------------------------------- /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 D:\Softwares\Study_Softwares\android-studio-document\sdk/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/recyclerview/Adapter.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.recyclerview; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.support.v7.widget.CardView; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.support.v7.widget.StaggeredGridLayoutManager; 8 | import android.util.Log; 9 | import android.view.GestureDetector; 10 | import android.view.LayoutInflater; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.ViewTreeObserver; 15 | import android.view.animation.DecelerateInterpolator; 16 | import android.widget.ImageView; 17 | import android.widget.TextView; 18 | 19 | import com.martinrgb.waterfalllayout.R; 20 | import com.zach.salman.springylib.springyRecyclerView.SpringyAdapterAnimationType; 21 | import com.zach.salman.springylib.springyRecyclerView.SpringyAdapterAnimator; 22 | 23 | import java.util.ArrayList; 24 | import java.util.Collections; 25 | import java.util.List; 26 | import java.util.Locale; 27 | 28 | import github.nisrulz.recyclerviewhelper.RVHAdapter; 29 | import github.nisrulz.recyclerviewhelper.RVHItemClickListener; 30 | import github.nisrulz.recyclerviewhelper.RVHViewHolder; 31 | 32 | import com.facebook.rebound.SimpleSpringListener; 33 | import com.facebook.rebound.Spring; 34 | import com.facebook.rebound.SpringConfig; 35 | import com.facebook.rebound.SpringSystem; 36 | import com.facebook.rebound.SpringUtil; 37 | 38 | /** 39 | * Created by Zach on 6/30/2017. 40 | */ 41 | 42 | 43 | //public class Adapter extends BaseRecyclerAdapter 44 | public class Adapter extends RecyclerView.Adapter implements RVHAdapter { 45 | 46 | 47 | 48 | private SpringyAdapterAnimator mAnimator; 49 | public final List CardList = new ArrayList<>(); 50 | 51 | public ImageView icon,cardImg; 52 | public CardView card; 53 | public TextView title; 54 | public boolean disableItemEventListener = false; 55 | private Context mContext; 56 | private boolean hasVibrate = false; 57 | private static final int VIEW_TYPE_ONE = 1; 58 | private static final int VIEW_TYPE_TWO = 2; 59 | private RecyclerView rv; 60 | 61 | private View prevDragView; 62 | private int prevLongPressPosition; 63 | public boolean canSwap = false; 64 | private long downTime, upTime; 65 | 66 | 67 | public Adapter(List cardList , RecyclerView recyclerView,onItemEventListener listener) { 68 | this.mListener = listener; 69 | //this.CardList = cardList; 70 | //notifyData(cardList); 71 | this.rv = recyclerView; 72 | mAnimator = new SpringyAdapterAnimator(recyclerView); 73 | mAnimator.setSpringAnimationType(SpringyAdapterAnimationType.SLIDE_FROM_BOTTOM); 74 | mAnimator.addConfig(100,18); 75 | } 76 | 77 | public void notifyData(List poiItemList) { 78 | if (poiItemList != null) { 79 | int previousSize = CardList.size(); 80 | CardList.clear(); 81 | notifyItemRangeRemoved(0, previousSize); 82 | CardList.addAll(poiItemList); 83 | notifyItemRangeInserted(0, poiItemList.size()); 84 | Log.e("ValueNotify",String.valueOf(CardList.size())); 85 | 86 | } 87 | } 88 | 89 | @Override 90 | public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 91 | 92 | mContext = parent.getContext(); 93 | 94 | 95 | View itemView = LayoutInflater.from(parent.getContext()) 96 | .inflate(R.layout.list_item, parent, false); 97 | 98 | itemView.setClipToOutline(false); 99 | return new ItemViewHolder(itemView); 100 | 101 | 102 | } 103 | 104 | 105 | 106 | int[] startPosition = new int[2]; 107 | private int lastAnimatedPosition = -1; 108 | @Override 109 | public void onViewDetachedFromWindow(final ItemViewHolder holder) { 110 | holder.clearAnimation(); 111 | } 112 | 113 | @Override 114 | public void onBindViewHolder(final ItemViewHolder holder, final int position) { 115 | 116 | 117 | if(position == 0){ 118 | card.getLayoutParams().width = 468; 119 | card.getLayoutParams().height = 720; //720 120 | card.requestLayout(); 121 | } 122 | 123 | 124 | cardImg.setImageResource(CardList.get(position).imgSrc); 125 | 126 | if(CardList.get(position).name !=null){ 127 | title.setText(CardList.get(position).name); 128 | icon.setImageResource(CardList.get(position).iconSrc); 129 | 130 | } 131 | 132 | holder.itemTagIndex = CardList.get(position).tagNum; 133 | 134 | mAnimator.setSpringAnimationType(SpringyAdapterAnimationType.NULL); 135 | mAnimator.onSpringItemCreate(holder.itemView,position); 136 | 137 | //Orig 138 | holder.itemView.setOnClickListener(new View.OnClickListener() { 139 | @Override 140 | public void onClick(View v) { 141 | 142 | // Updating old as well as new positions 143 | 144 | if(!disableItemEventListener) { 145 | //int finalOrigPos = CardList.get(holder.getAdapterPosition()).tagNum; 146 | mListener.onItemClick(v, CardList.get(holder.getAdapterPosition()), holder.getAdapterPosition(),holder.itemTagIndex); 147 | hasSwaped = false; 148 | 149 | } 150 | 151 | } 152 | }); 153 | 154 | } 155 | 156 | @Override 157 | public int getItemCount() { 158 | return CardList.size(); 159 | } 160 | 161 | public void onItemAnimContinue(){ 162 | 163 | final int size = mAnimator.mSpringListArray.size(); 164 | 165 | mAnimator.setSpringAnimationType(SpringyAdapterAnimationType.SLIDE_FROM_BOTTOM); 166 | 167 | for(int i =0;i 4 ){ 216 | rv.getLayoutParams().height = 6000; 217 | rv.requestLayout(); 218 | } 219 | else if(rv.getLayoutManager().findViewByPosition(0) == null){ 220 | } 221 | 222 | 223 | 224 | prevDragView = rv.findViewHolderForAdapterPosition(position).itemView; 225 | prevDragView.setTranslationZ(10000); 226 | 227 | if(!disableItemEventListener) { 228 | mListener.onItemSelected(position); 229 | } 230 | 231 | } 232 | 233 | public void clearAnimation() { 234 | container.clearAnimation(); 235 | } 236 | 237 | 238 | @Override 239 | public void onItemClear() { 240 | System.out.println("Item is unselected"); 241 | mListener.onItemOnDragging(false); 242 | 243 | 244 | if(hasSelect && rv.getLayoutParams().height != 2340){ 245 | rv.getLayoutParams().height = 2340; 246 | rv.requestLayout(); 247 | hasSelect = false; 248 | } 249 | 250 | 251 | if(prevDragView != null){ 252 | prevDragView.setTranslationZ(0); 253 | } 254 | 255 | if(!disableItemEventListener) { 256 | mListener.onItemUnselected(prevLongPressPosition); 257 | mListener.onItemLongPressed(false, prevLongPressPosition); 258 | 259 | } 260 | 261 | //mListener.onItemUnselected(prevLongPressPosition); 262 | //mListener.onItemLongPressed(false, prevLongPressPosition); 263 | 264 | 265 | } 266 | } 267 | 268 | private boolean hasSwaped = false; 269 | private int removedPos; 270 | private boolean hasRemoved = false; 271 | private boolean hasSelect = false; 272 | private int firstSwapPos,secondSwapPos; 273 | 274 | // ###### Delete Item ###### 275 | private void remove(int position) { 276 | 277 | 278 | Log.e("Value",String.valueOf(CardList.size())); 279 | 280 | CardList.remove(position); 281 | 282 | mListener.onItemRemove(position,CardList.size()); 283 | notifyItemRemoved(position); 284 | 285 | 286 | 287 | 288 | if(hasSelect && rv.getLayoutParams().height != 2340){ 289 | 290 | if(CardList.size() == 2){ 291 | rv.getLayoutParams().height = 10000; 292 | rv.requestLayout(); 293 | } 294 | else{ 295 | 296 | rv.getLayoutParams().height = 2340; 297 | rv.requestLayout(); 298 | } 299 | 300 | } 301 | 302 | if(hasSwaped){ 303 | 304 | if(position == firstSwapPos){ 305 | System.out.println("Item is removed at " + secondSwapPos); 306 | } 307 | else if(position == secondSwapPos){ 308 | System.out.println("Item is removed at " + firstSwapPos); 309 | } 310 | } 311 | else{ 312 | System.out.println("Item is removed at hasentSwp " + position); 313 | } 314 | } 315 | 316 | 317 | @Override 318 | public void onItemDismiss(int position, int direction) { 319 | if(position != 0){ 320 | remove(position); 321 | hasRemoved = true; 322 | removedPos = position; 323 | Log.e("removePos",String.valueOf(removedPos)); 324 | 325 | } 326 | } 327 | 328 | // ###### Swap Item (deprecated) ###### 329 | private void swap(int firstPosition, int secondPosition) { 330 | System.out.println("Item " + Integer.valueOf(firstPosition) + " and " + Integer.valueOf(secondPosition) + " is swapped"); 331 | 332 | 333 | Collections.swap(CardList, firstPosition, secondPosition); 334 | mListener.onItemSwap(firstPosition, secondPosition); 335 | notifyItemMoved(firstPosition, secondPosition); 336 | 337 | Log.e("first",String.valueOf(firstPosition)); 338 | Log.e("second",String.valueOf(secondPosition)); 339 | 340 | hasSwaped = true; 341 | firstSwapPos = firstPosition; 342 | secondSwapPos = secondPosition; 343 | } 344 | 345 | @Override 346 | public boolean onItemMove(int fromPosition, int toPosition) { 347 | if(CardList.size() != 0 && canSwap) { 348 | swap(fromPosition, toPosition); 349 | } 350 | 351 | 352 | return false; 353 | } 354 | 355 | 356 | public void outTransition(){ 357 | final int size = this.CardList.size(); 358 | 359 | 360 | new Handler().postDelayed(new Runnable() { 361 | @Override 362 | public void run() { 363 | 364 | if (size > 0) { 365 | for (int i = 0; i < size; i++) { 366 | if(CardList.size()>0){ 367 | 368 | CardList.remove(0); 369 | } 370 | } 371 | 372 | notifyItemRangeRemoved(0, size); 373 | } 374 | } 375 | },300 ); 376 | } 377 | 378 | public void outTransition2(){ 379 | final int size = this.CardList.size(); 380 | 381 | 382 | mAnimator.setSpringAnimationType(SpringyAdapterAnimationType.SLIDE_FROM_TOP); 383 | mAnimator.DELETE_PER_DELAY = 0; 384 | 385 | for(int i =0;i 0) { 398 | for (int i = 0; i < size; i++) { 399 | if(CardList.size()>0){ 400 | 401 | CardList.remove(0); 402 | } 403 | } 404 | 405 | notifyItemRangeRemoved(0, size); 406 | } 407 | } 408 | },300 ); 409 | } 410 | 411 | public void outTransition3(){ 412 | final int size = this.CardList.size(); 413 | 414 | 415 | mAnimator.setSpringAnimationType(SpringyAdapterAnimationType.SLIDE_FROM_BOTTOM); 416 | mAnimator.DELETE_PER_DELAY = 0; 417 | 418 | for(int i =0;i 0) { 431 | for (int i = 0; i < size; i++) { 432 | if(CardList.size()>0){ 433 | 434 | CardList.remove(0); 435 | } 436 | } 437 | 438 | notifyItemRangeRemoved(0, size); 439 | } 440 | } 441 | },300 ); 442 | } 443 | 444 | public void removeAll(){ 445 | final int size = this.CardList.size(); 446 | final int size2 = mAnimator.mSpringListArray.size(); 447 | 448 | mAnimator.setSpringAnimationType(SpringyAdapterAnimationType.SPREAD); 449 | mAnimator.DELETE_PER_DELAY = 50; 450 | 451 | for(int i =0;i 0) { 464 | for (int i = 0; i < size; i++) { 465 | CardList.remove(0); 466 | } 467 | 468 | notifyItemRangeRemoved(0, size); 469 | } 470 | } 471 | },300 ); 472 | 473 | 474 | } 475 | 476 | 477 | // ###### Drag Item ###### 478 | private int lastDragPos; 479 | @Override 480 | public void onItemSwipeDrag(float dX,float dY,int position) { 481 | if(rv.getLayoutManager().findViewByPosition(position) !=null){ 482 | mListener.onItemOnDragging(true); 483 | 484 | rv.getLayoutManager().findViewByPosition(position).findViewById(R.id.iv_card).setElevation(18 + Math.abs(dX)/200); 485 | rv.getLayoutManager().findViewByPosition(position).findViewById(R.id.iv_container).setAlpha(1 - Math.abs(dX/500)); 486 | rv.getLayoutManager().findViewByPosition(position).findViewById(R.id.iv_container).setTranslationX(-dX); 487 | } 488 | } 489 | 490 | // ###### Long Press Item (deprecated) ###### 491 | @Override 492 | public void onItemLongPress(float dX,float dY,int position) { 493 | 494 | if(!disableItemEventListener){ 495 | mListener.onItemOnDragging(false); 496 | mListener.onItemLongPressed(true,position); 497 | } 498 | 499 | prevLongPressPosition = position; 500 | 501 | } 502 | 503 | 504 | private final onItemEventListener mListener; 505 | public interface onItemEventListener { 506 | void onItemSelected(int position); 507 | void onItemUnselected(int position); 508 | void onItemLongPressed(boolean boo,int position); 509 | void onItemClick(View view, Card item,int position,int orignPos); 510 | void onItemRemove(int pos,int itemSize); 511 | void onItemSwap(int firstPosition, int secondPosition); 512 | void onItemOnDragging(boolean boo); 513 | } 514 | 515 | 516 | } -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/recyclerview/Card.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.recyclerview; 2 | 3 | /** 4 | * Created by lcodecore on 2016/12/7. 5 | */ 6 | 7 | public class Card { 8 | public String name; 9 | public int imgSrc; 10 | public int iconSrc; 11 | 12 | public int tagNum; 13 | 14 | public Card(String name, int imgSrc,int iconSrc,int tagNum) { 15 | this.name = name; 16 | this.imgSrc = imgSrc; 17 | this.iconSrc = iconSrc; 18 | this.tagNum = tagNum; 19 | } 20 | 21 | public Card(int imgSrc) { 22 | this.name = null; 23 | this.imgSrc = imgSrc; 24 | this.iconSrc = 0; 25 | } 26 | 27 | 28 | public int getTagNum() { 29 | return tagNum; 30 | } 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/recyclerview/SmoothScrollStaggerGridLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.recyclerview; 2 | 3 | import android.content.Context; 4 | import android.graphics.PointF; 5 | import android.support.v7.widget.LinearSmoothScroller; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.support.v7.widget.StaggeredGridLayoutManager; 8 | import android.util.DisplayMetrics; 9 | import android.util.Log; 10 | 11 | /** 12 | * Created by MartinRGB on 2017/10/19. 13 | */ 14 | 15 | 16 | public class SmoothScrollStaggerGridLayoutManager extends StaggeredGridLayoutManager { 17 | private float MILLISECONDS_PER_INCH = 0.03f; 18 | private Context contxt; 19 | private boolean isScrollEnabled = true; 20 | 21 | public SmoothScrollStaggerGridLayoutManager(int spanCount, int orientation) { 22 | super(spanCount,orientation); 23 | } 24 | 25 | public void setScrollEnabled(boolean flag) { 26 | this.isScrollEnabled = flag; 27 | } 28 | 29 | @Override 30 | public void collectAdjacentPrefetchPositions (int dx,int dy,RecyclerView.State state, RecyclerView.LayoutManager.LayoutPrefetchRegistry layoutPrefetchRegistry){ 31 | 32 | try { 33 | super.collectAdjacentPrefetchPositions(dx, dy, state, layoutPrefetchRegistry); 34 | } catch (Throwable e) { 35 | e.printStackTrace(); 36 | } 37 | 38 | } 39 | 40 | 41 | @Override 42 | public boolean canScrollVertically() { 43 | //Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll 44 | return isScrollEnabled && super.canScrollVertically(); 45 | } 46 | 47 | @Override 48 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 49 | try { 50 | super.onLayoutChildren(recycler, state); 51 | } catch (IndexOutOfBoundsException e) { 52 | Log.e("probe", "meet a IOOBE in RecyclerView"); 53 | e.printStackTrace(); 54 | } 55 | } 56 | 57 | 58 | @Override 59 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { 60 | LinearSmoothScroller linearSmoothScroller = 61 | new LinearSmoothScroller(recyclerView.getContext()) { 62 | @Override 63 | public PointF computeScrollVectorForPosition(int targetPosition) { 64 | return SmoothScrollStaggerGridLayoutManager.this 65 | .computeScrollVectorForPosition(targetPosition); 66 | } 67 | 68 | //This returns the milliseconds it takes to 69 | //scroll one pixel. 70 | @Override 71 | protected float calculateSpeedPerPixel 72 | (DisplayMetrics displayMetrics) { 73 | return MILLISECONDS_PER_INCH / displayMetrics.density; 74 | //返回滑动一个pixel需要多少毫秒 75 | } 76 | 77 | }; 78 | linearSmoothScroller.setTargetPosition(position); 79 | startSmoothScroll(linearSmoothScroller); 80 | } 81 | 82 | 83 | public void setSpeedSlow() { 84 | //自己在这里用density去乘,希望不同分辨率设备上滑动速度相同 85 | //0.3f是自己估摸的一个值,可以根据不同需求自己修改 86 | MILLISECONDS_PER_INCH = contxt.getResources().getDisplayMetrics().density * 0.3f; 87 | } 88 | 89 | public void setSpeedFast() { 90 | MILLISECONDS_PER_INCH = contxt.getResources().getDisplayMetrics().density * 0.03f; 91 | } 92 | } -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/recyclerview/TouchRecyclerView.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.recyclerview; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.MotionEvent; 8 | 9 | 10 | public class TouchRecyclerView extends RecyclerView 11 | { 12 | // Depending on how you're creating this View, 13 | // you might need to specify additional constructors. 14 | public TouchRecyclerView(Context context, AttributeSet attrs) 15 | { 16 | super(context, attrs); 17 | } 18 | 19 | private OnNoChildClickListener listener; 20 | public interface OnNoChildClickListener 21 | { 22 | public void onNoChildClick(float posX,float posY); 23 | } 24 | 25 | 26 | public void setOnNoChildClickListener(OnNoChildClickListener listener) 27 | { 28 | this.listener = listener; 29 | } 30 | 31 | 32 | @Override 33 | public boolean dispatchTouchEvent(MotionEvent event) 34 | { 35 | // The findChildViewUnder() method returns null if the touch event 36 | // occurs outside of a child View. 37 | // Change the MotionEvent action as needed. Here we use ACTION_DOWN 38 | // as a simple, naive indication of a click. 39 | if (event.getAction() == MotionEvent.ACTION_DOWN 40 | && findChildViewUnder(event.getX(), event.getY()) == null) 41 | { 42 | if (listener != null) 43 | { 44 | listener.onNoChildClick(event.getX(),event.getY()); 45 | } 46 | 47 | 48 | } 49 | return super.dispatchTouchEvent(event); 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/AppBlockCanaryContext.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.content.Context; 4 | 5 | import com.github.moduth.blockcanary.BlockCanaryContext; 6 | import com.github.moduth.blockcanary.internal.BlockInfo; 7 | 8 | import java.io.File; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by MartinRGB on 2017/10/30. 14 | */ 15 | 16 | public class AppBlockCanaryContext extends BlockCanaryContext { 17 | 18 | /** 19 | * Implement in your project. 20 | * 21 | * @return Qualifier which can specify this installation, like version + flavor. 22 | */ 23 | public String provideQualifier() { 24 | return "unknown"; 25 | } 26 | 27 | /** 28 | * Implement in your project. 29 | * 30 | * @return user id 31 | */ 32 | public String provideUid() { 33 | return "uid"; 34 | } 35 | 36 | /** 37 | * Network type 38 | * 39 | * @return {@link String} like 2G, 3G, 4G, wifi, etc. 40 | */ 41 | public String provideNetworkType() { 42 | return "unknown"; 43 | } 44 | 45 | /** 46 | * Config monitor duration, after this time BlockCanary will stop, use 47 | * with {@code BlockCanary}'s isMonitorDurationEnd 48 | * 49 | * @return monitor last duration (in hour) 50 | */ 51 | public int provideMonitorDuration() { 52 | return -1; 53 | } 54 | 55 | /** 56 | * Config block threshold (in millis), dispatch over this duration is regarded as a BLOCK. You may set it 57 | * from performance of device. 58 | * 59 | * @return threshold in mills 60 | */ 61 | public int provideBlockThreshold() { 62 | return 1000; 63 | } 64 | 65 | /** 66 | * Thread stack dump interval, use when block happens, BlockCanary will dump on main thread 67 | * stack according to current sample cycle. 68 | *

69 | * Because the implementation mechanism of Looper, real dump interval would be longer than 70 | * the period specified here (especially when cpu is busier). 71 | *

72 | * 73 | * @return dump interval (in millis) 74 | */ 75 | public int provideDumpInterval() { 76 | return provideBlockThreshold(); 77 | } 78 | 79 | /** 80 | * Path to save log, like "/blockcanary/", will save to sdcard if can. 81 | * 82 | * @return path of log files 83 | */ 84 | public String providePath() { 85 | return "/blockcanary/"; 86 | } 87 | 88 | /** 89 | * If need notification to notice block. 90 | * 91 | * @return true if need, else if not need. 92 | */ 93 | public boolean displayNotification() { 94 | return true; 95 | } 96 | 97 | /** 98 | * Implement in your project, bundle files into a zip file. 99 | * 100 | * @param src files before compress 101 | * @param dest files compressed 102 | * @return true if compression is successful 103 | */ 104 | public boolean zip(File[] src, File dest) { 105 | return false; 106 | } 107 | 108 | /** 109 | * Implement in your project, bundled log files. 110 | * 111 | * @param zippedFile zipped file 112 | */ 113 | public void upload(File zippedFile) { 114 | throw new UnsupportedOperationException(); 115 | } 116 | 117 | 118 | /** 119 | * Packages that developer concern, by default it uses process name, 120 | * put high priority one in pre-order. 121 | * 122 | * @return null if simply concern only package with process name. 123 | */ 124 | public List concernPackages() { 125 | return null; 126 | } 127 | 128 | /** 129 | * Filter stack without any in concern package, used with @{code concernPackages}. 130 | * 131 | * @return true if filter, false it not. 132 | */ 133 | public boolean filterNonConcernStack() { 134 | return false; 135 | } 136 | 137 | /** 138 | * Provide white list, entry in white list will not be shown in ui list. 139 | * 140 | * @return return null if you don't need white-list filter. 141 | */ 142 | public List provideWhiteList() { 143 | LinkedList whiteList = new LinkedList<>(); 144 | whiteList.add("org.chromium"); 145 | return whiteList; 146 | } 147 | 148 | /** 149 | * Whether to delete files whose stack is in white list, used with white-list. 150 | * 151 | * @return true if delete, false it not. 152 | */ 153 | public boolean deleteFilesInWhiteList() { 154 | return true; 155 | } 156 | 157 | /** 158 | * Block interceptor, developer may provide their own actions. 159 | */ 160 | public void onBlock(Context context, BlockInfo blockInfo) { 161 | 162 | } 163 | } -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/BitmapUtil.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.util.LruCache; 7 | import android.view.View; 8 | 9 | import java.util.UUID; 10 | 11 | 12 | public class BitmapUtil { 13 | private static LruCache mMemoryCache; 14 | static { 15 | final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 16 | final int cacheSize = maxMemory / 8; // Use 1/8th of the available memory for this memory cache. 17 | mMemoryCache = new LruCache(cacheSize) { 18 | @Override 19 | protected int sizeOf(String key, Bitmap bitmap) { 20 | return bitmap.getByteCount() / 1024; // The cache size will be measured in kilobytes rather than number of items. 21 | } 22 | }; 23 | } 24 | 25 | public static void storeBitmapInIntent(Bitmap bitmap, Intent intent) { 26 | String key = "bitmap_" + UUID.randomUUID(); 27 | storeBitmapInMemCache(key, bitmap); 28 | intent.putExtra("bitmap_id", key); 29 | 30 | // ByteArrayOutputStream bs = new ByteArrayOutputStream(); 31 | // bitmap.compress(Bitmap.CompressFormat.PNG, 0, bs); 32 | // intent.putExtra("background", bs.toByteArray()); 33 | } 34 | 35 | public static void storeBitmapInMemCache(String key, Bitmap bitmap) { 36 | if (getBitmapFromMemCache(key) == null) { 37 | mMemoryCache.put(key, bitmap); 38 | } 39 | } 40 | 41 | public static Bitmap getBitmapFromMemCache(String key) { 42 | return mMemoryCache.get(key); 43 | } 44 | 45 | public static Bitmap fetchBitmapFromIntent(Intent intent) { 46 | String key = intent.getStringExtra("bitmap_id"); 47 | // byte[] byteArray = intent.getByteArrayExtra("background"); 48 | // Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); 49 | return getBitmapFromMemCache(key); 50 | } 51 | 52 | public static Bitmap createBitmap(View v) { 53 | Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); 54 | Canvas canvas = new Canvas(bitmap); 55 | v.draw(canvas); 56 | return bitmap; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/DensityUtil.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.content.Context; 4 | import android.graphics.Point; 5 | import android.view.Display; 6 | import android.view.WindowManager; 7 | 8 | public class DensityUtil { 9 | private static int screenWidth = 0; 10 | private static int screenHeight = 0; 11 | public static int dip2px(Context var0, float var1) { 12 | float var2 = var0.getResources().getDisplayMetrics().density; 13 | return (int)(var1 * var2 + 0.5F); 14 | } 15 | public static int px2dip(Context var0, float var1) { 16 | float var2 = var0.getResources().getDisplayMetrics().density; 17 | return (int)(var1 / var2 + 0.5F); 18 | } 19 | public static int sp2px(Context var0, float var1) { 20 | float var2 = var0.getResources().getDisplayMetrics().scaledDensity; 21 | return (int)(var1 * var2 + 0.5F); 22 | } 23 | public static int px2sp(Context var0, float var1) { 24 | float var2 = var0.getResources().getDisplayMetrics().scaledDensity; 25 | return (int)(var1 / var2 + 0.5F); 26 | } 27 | public static int getScreenWidth(Context c) { 28 | if (screenWidth == 0) { 29 | WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE); 30 | Display display = wm.getDefaultDisplay(); 31 | Point size = new Point(); 32 | display.getSize(size); 33 | screenWidth = size.x; 34 | } 35 | return screenWidth; 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/LogUtil.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.util.Log; 4 | import android.widget.Toast; 5 | 6 | 7 | /** 8 | * Created by lcodecore on 2017/2/28. 9 | */ 10 | 11 | public class LogUtil { 12 | public static void show(Object c,String msg){ 13 | Log.e(String.valueOf(c),msg); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/MultiGestureDetector.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | import android.provider.Settings; 7 | import android.util.Log; 8 | import android.view.GestureDetector; 9 | import android.view.MotionEvent; 10 | import android.view.VelocityTracker; 11 | import android.view.View; 12 | import android.widget.Toast; 13 | 14 | /** 15 | * Created by MartinRGB on 2017/9/11. 16 | */ 17 | 18 | public class MultiGestureDetector implements GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener,View.OnTouchListener{ 19 | 20 | private VelocityTracker mVelocityTracker; 21 | private static final int SWIPE_DISTANCE_THRESHOLD = 200; 22 | private static final int SWIPE_VELOCITY_THRESHOLD = 2000; 23 | private static final int LOG_TYPE_I = 0; 24 | private static final int LOG_TYPE_E = 1; 25 | private static final String TAG = "MultiGestureDetector"; 26 | private GestureDetector mGestureDetector; 27 | private boolean firstJudge = false; 28 | 29 | //创建监听器借口 30 | public interface SimpleGestureListener 31 | { 32 | void onDown(MotionEvent event); 33 | void onLongPress(MotionEvent event); 34 | void onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY,float velocityX,float velocityY); 35 | // void onSwipeLeft(float velocity); 36 | // void onSwipeRight(float velocity); 37 | // void onSwipeBottom(float velocity); 38 | void onSwipeTop(float velocity); 39 | void onUp(MotionEvent event); 40 | // void onDoubleTap(MotionEvent event); 41 | void onSwipeTopFix(); 42 | void onVelocityStop(boolean boo); 43 | void onTriggerJudge(boolean boo); 44 | } 45 | //实例化一个监听器的数值为空 46 | private SimpleGestureListener mSimpleGestureListener = null; 47 | 48 | public MultiGestureDetector(SimpleGestureListener simpleGestureListener) { 49 | mGestureDetector = new GestureDetector(this); 50 | mSimpleGestureListener = simpleGestureListener; 51 | } 52 | 53 | public boolean onDown(MotionEvent event) { 54 | return true; 55 | } 56 | 57 | public void onShowPress(MotionEvent event) { 58 | printTag(TAG,true,LOG_TYPE_I,event); 59 | //Reset 60 | //mSimpleGestureListener.onTriggerJudge(false); 61 | latestTrackedVelocityX = 0; 62 | latestTrackedVelocityY = 0; 63 | scrollInProgress = false; 64 | firstJudge = false; 65 | 66 | } 67 | 68 | public void onLongPress(MotionEvent event) { 69 | //mSimpleGestureListener.onLongPress(event); 70 | } 71 | 72 | 73 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 74 | mSimpleGestureListener.onScroll(e1,e2,e2.getX()-e1.getX(),e2.getY()-e1.getY(),xVelocity,yVelocity); 75 | return true; 76 | } 77 | 78 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 79 | 80 | boolean result = false; 81 | try { 82 | float diffY = e2.getY() - e1.getY(); 83 | float diffX = e2.getX() - e1.getX(); 84 | if (Math.abs(diffX) > Math.abs(diffY)) { 85 | if (Math.abs(diffX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { 86 | if (diffX > 0) { 87 | //mSimpleGestureListener.onSwipeRight(velocityX); 88 | //Log.e("SFACT","Swipe Right,Velocity is" + velocityX); 89 | } else { 90 | //mSimpleGestureListener.onSwipeLeft(velocityX); 91 | //Log.e("SFACT","Swipe Left,Velocity is" + velocityX); 92 | } 93 | result = true; 94 | } 95 | 96 | } 97 | 98 | else if (Math.abs(diffX) < Math.abs(diffY)){ 99 | 100 | if (Math.abs(diffY) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { 101 | if (diffY > 0) { 102 | //Log.e("SFACT","Swipe Bottom,Velocity is" + velocityY); 103 | // mSimpleGestureListener.onSwipeBottom(velocityY); 104 | } else { 105 | //Log.e("SFACT","Swipe Top,Velocity is" + velocityY); 106 | mSimpleGestureListener.onSwipeTop(velocityY); 107 | 108 | } 109 | result = true; 110 | } 111 | else{ 112 | Log.e("SFACT","Should Fix Error"); 113 | mSimpleGestureListener.onSwipeTopFix(); 114 | } 115 | 116 | } 117 | else{ 118 | } 119 | 120 | } catch (Exception exception) { 121 | exception.printStackTrace(); 122 | } 123 | 124 | //printTag(TAG,true,LOG_TYPE_E,null); 125 | return result; 126 | } 127 | 128 | private boolean hasRecycled = false; 129 | private float xVelocity,yVelocity; 130 | private timeCheckHandler mTimeCheckHandler = new timeCheckHandler(); 131 | 132 | public boolean onTouch(View v, MotionEvent event) { 133 | 134 | //Log.v("ADAM", "ontouch: " + event.getAction()); 135 | 136 | boolean detectedUp = event.getAction() == MotionEvent.ACTION_UP; 137 | 138 | if(event.getAction() == 0){ 139 | mSimpleGestureListener.onDown(event); 140 | mSimpleGestureListener.onTriggerJudge(false); 141 | 142 | if (mVelocityTracker == null) { 143 | // Retrieve a new VelocityTracker object to watch the velocity of a motion. 144 | mVelocityTracker = VelocityTracker.obtain(); 145 | 146 | //// the following instruction resets the Time Check clock // the clock is first started 147 | mTimeCheckHandler.sleep(timeCheckInterval); 148 | latestTrackedVelocityX = 0; 149 | latestTrackedVelocityY = 0; 150 | 151 | } else { 152 | // Reset the velocity tracker back to its initial state. 153 | mVelocityTracker.clear(); 154 | hasRecycled = false; 155 | 156 | } 157 | 158 | //Log.v("ADAM", "Down"); 159 | } 160 | else if (event.getAction() == 1) { 161 | //Log.v("ADAM", "Up"); 162 | mSimpleGestureListener.onUp(event); 163 | // Return a VelocityTracker object back to be re-used by others. 164 | 165 | mSimpleGestureListener.onUp(event); 166 | // Return a VelocityTracker object back to be re-used by others. 167 | 168 | if(!hasRecycled){ 169 | mVelocityTracker.clear(); 170 | mVelocityTracker.recycle(); 171 | mVelocityTracker = null; 172 | hasRecycled = true; 173 | mSimpleGestureListener.onTriggerJudge(false); 174 | scrollInProgress = false; 175 | } 176 | 177 | //Add userMovement 178 | //mVelocityTracker.addMovement(event); 179 | } 180 | else if (event.getAction() == 2) { 181 | //Log.v("ADAM", "Scroll"); 182 | mVelocityTracker.addMovement(event); 183 | // When you want to determine the velocity, call 184 | // computeCurrentVelocity(). Then call getXVelocity() 185 | // and getYVelocity() to retrieve the velocity for each pointer ID. 186 | mVelocityTracker.computeCurrentVelocity(16); 187 | // Log velocity of pixels per second 188 | xVelocity = mVelocityTracker.getXVelocity(0); 189 | yVelocity = mVelocityTracker.getYVelocity(0); 190 | 191 | if(Math.abs(Math.abs(xVelocity) - latestTrackedVelocityX) + Math.abs(Math.abs(yVelocity) - latestTrackedVelocityY) > translationThereshold ){ 192 | 193 | if(Math.abs(xVelocity) > 0.5 || Math.abs(yVelocity) > 0.5){ 194 | scrollInProgress = true; 195 | mSimpleGestureListener.onVelocityStop(false); 196 | mSimpleGestureListener.onTriggerJudge(false); 197 | } 198 | 199 | } 200 | 201 | if(!scrollInProgress && (latestTrackedVelocityX + latestTrackedVelocityY) > 1.4){ 202 | 203 | if(Math.abs(System.currentTimeMillis() - stopTime) > 200){ 204 | 205 | scrollInProgress = true; 206 | 207 | } 208 | } 209 | 210 | 211 | long now = System.currentTimeMillis(); 212 | latestScrollEventTime = now; 213 | } 214 | 215 | else if (event.getAction() == MotionEvent.ACTION_CANCEL) { 216 | Log.v("ADAM", "Cancel2"); 217 | mSimpleGestureListener.onUp(event); 218 | 219 | // Return a VelocityTracker object back to be re-used by others. 220 | if(!hasRecycled){ 221 | mVelocityTracker.clear(); 222 | mVelocityTracker.recycle(); 223 | mVelocityTracker = null; 224 | hasRecycled = true; 225 | } 226 | 227 | } 228 | else{ 229 | // Log.v("ADAM", "Else"); 230 | } 231 | 232 | if (!mGestureDetector.onTouchEvent(event) && detectedUp) { 233 | Log.v("ADAM", "Cancel"); 234 | mSimpleGestureListener.onUp(event); 235 | 236 | // Return a VelocityTracker object back to be re-used by others. 237 | if(!hasRecycled){ 238 | mVelocityTracker.clear(); 239 | mVelocityTracker.recycle(); 240 | mVelocityTracker = null; 241 | hasRecycled = true; 242 | } 243 | return true; 244 | } 245 | 246 | return true; 247 | 248 | // printTag(TAG,true,LOG_TYPE_I,event); 249 | } 250 | 251 | 252 | public boolean onSingleTapUp(MotionEvent event) { 253 | //printTag(TAG,true,LOG_TYPE_I,event); 254 | 255 | //mSimpleGestureListener.onUp(event); 256 | return false; 257 | } 258 | 259 | @Override 260 | public boolean onSingleTapConfirmed(MotionEvent event) { 261 | //printTag(TAG,true,LOG_TYPE_I,event); 262 | return false; 263 | } 264 | 265 | @Override 266 | public boolean onDoubleTap(MotionEvent event) { 267 | //printTag(TAG,true,LOG_TYPE_I,event); 268 | return false; 269 | } 270 | 271 | @Override 272 | public boolean onDoubleTapEvent(MotionEvent event) { 273 | //printTag(TAG,true,LOG_TYPE_I,event); 274 | //mSimpleGestureListener.onDoubleTap(event); 275 | return false; 276 | } 277 | 278 | 279 | // ############ Time Check Handler ############ 280 | //Changing 1 - Trending 281 | //private long timeCheckInterval = 100; // 检测间隔 282 | //Changing 2 - Trending + StopJudge 283 | private long timeCheckInterval = 16; // 检测间隔 284 | private long scrollEndInterval = 20; // 滚动结束后保留时间 285 | public long latestScrollEventTime; 286 | private float latestTrackedVelocityX; 287 | private float latestTrackedVelocityY; 288 | public boolean scrollInProgress = false; 289 | private float translationThereshold = 1.0f; 290 | private long stopTime; 291 | private Runnable myRunnable = new Runnable() { 292 | @Override 293 | public void run() { 294 | //Do Something 295 | long now = System.currentTimeMillis(); 296 | if (scrollInProgress && (now>latestScrollEventTime+scrollEndInterval) && Math.abs(Math.abs(xVelocity) - latestTrackedVelocityX) + Math.abs(Math.abs(yVelocity) - latestTrackedVelocityY) < translationThereshold ) { 297 | scrollInProgress = false; 298 | 299 | mSimpleGestureListener.onTriggerJudge(true); 300 | 301 | //Log.e("String2","trigger this;"); 302 | stopTime = now; 303 | 304 | 305 | } 306 | 307 | latestTrackedVelocityX = Math.abs(xVelocity); 308 | latestTrackedVelocityY = Math.abs(yVelocity); 309 | 310 | 311 | if(scrollInProgress){ 312 | mSimpleGestureListener.onVelocityStop(false); 313 | }else{ 314 | mSimpleGestureListener.onVelocityStop(true); 315 | } 316 | } 317 | }; 318 | 319 | //或者 使用 static Hanlder + 弱引用,或者在 Frag|Activity销毁时 remove 320 | public void removeHandler(){ 321 | mTimeCheckHandler.removeCallbacksAndMessages(null); 322 | } 323 | 324 | 325 | class timeCheckHandler extends Handler { 326 | 327 | 328 | @Override 329 | public void handleMessage(Message msg) { 330 | long now = System.currentTimeMillis(); 331 | if (scrollInProgress && (now>latestScrollEventTime+scrollEndInterval) && Math.abs(Math.abs(xVelocity) - latestTrackedVelocityX) + Math.abs(Math.abs(yVelocity) - latestTrackedVelocityY) < translationThereshold ) { 332 | scrollInProgress = false; 333 | 334 | mSimpleGestureListener.onTriggerJudge(true); 335 | 336 | //Log.e("String2","trigger this;"); 337 | stopTime = now; 338 | 339 | 340 | } 341 | 342 | latestTrackedVelocityX = Math.abs(xVelocity); 343 | latestTrackedVelocityY = Math.abs(yVelocity); 344 | 345 | 346 | if(scrollInProgress){ 347 | mSimpleGestureListener.onVelocityStop(false); 348 | }else{ 349 | mSimpleGestureListener.onVelocityStop(true); 350 | } 351 | 352 | this.sleep(timeCheckInterval); 353 | } 354 | 355 | public void sleep(long delayMillis) { 356 | this.removeMessages(0); 357 | sendMessageDelayed(obtainMessage(0), delayMillis); 358 | } 359 | } 360 | 361 | // ############ PrintTag ############ 362 | private void printTag(String tag,boolean show,int LOGTYPE,MotionEvent event){ 363 | 364 | final String suffix; 365 | 366 | if(event == null){ 367 | suffix = ""; 368 | } 369 | else{ 370 | int X = (int) event.getX(); 371 | int Y = (int) event.getY(); 372 | suffix = " - X: "+X+" Y: "+Y; 373 | } 374 | 375 | if(show){ 376 | 377 | if(LOGTYPE == 0){ 378 | 379 | StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); 380 | StackTraceElement e = stacktrace[3];//maybe this number needs to be corrected 381 | String methodName = e.getMethodName(); 382 | Log.i(tag,methodName + suffix); 383 | } 384 | else if(LOGTYPE == 1){ 385 | 386 | StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); 387 | StackTraceElement e = stacktrace[3];//maybe this number needs to be corrected 388 | String methodName = e.getMethodName(); 389 | Log.e(tag,methodName + suffix); 390 | } 391 | } 392 | else{ 393 | 394 | } 395 | } 396 | 397 | 398 | } 399 | -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/RoundedCornerLayout.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | /** 4 | * Created by MartinRGB on 2017/9/14. 5 | */ 6 | 7 | import android.animation.ValueAnimator; 8 | import android.content.Context; 9 | import android.graphics.Bitmap; 10 | import android.graphics.Canvas; 11 | import android.graphics.Color; 12 | import android.graphics.Matrix; 13 | import android.graphics.Paint; 14 | import android.graphics.Path; 15 | import android.graphics.RectF; 16 | import android.graphics.Region; 17 | import android.util.AttributeSet; 18 | import android.util.DisplayMetrics; 19 | import android.util.Log; 20 | import android.util.TypedValue; 21 | import android.view.View; 22 | import android.widget.FrameLayout; 23 | 24 | public class RoundedCornerLayout extends FrameLayout { 25 | //Init Radius in DP 26 | public float CORNER_RADIUS = 18f; 27 | //Draw Round Radius Boolean 28 | public boolean shouldRedraw = true; 29 | //Now Value for torlerance 30 | public float nowValue = 0; 31 | //Animation Time 32 | public float animTime = 400; 33 | private float cornerRadius; 34 | public long startTime; 35 | int framesPerSecond = 60; 36 | Paint paint; 37 | 38 | 39 | 40 | public RoundedCornerLayout(Context context) { 41 | super(context); 42 | init(context, null, 0); 43 | 44 | } 45 | 46 | public RoundedCornerLayout(Context context, AttributeSet attrs) { 47 | super(context, attrs); 48 | init(context, attrs, 0); 49 | } 50 | 51 | public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) { 52 | super(context, attrs, defStyle); 53 | init(context, attrs, defStyle); 54 | } 55 | 56 | private DisplayMetrics mMetrics; 57 | private void init(Context context, AttributeSet attrs, int defStyle) { 58 | DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 59 | 60 | mMetrics = metrics; 61 | 62 | 63 | cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics); 64 | setLayerType(View.LAYER_TYPE_HARDWARE, null); 65 | 66 | this.startTime = System.currentTimeMillis(); 67 | this.postInvalidate(); 68 | } 69 | 70 | 71 | @Override 72 | protected void onDraw(Canvas canvas){ 73 | 74 | } 75 | 76 | 77 | @Override 78 | protected void dispatchDraw(Canvas canvas) { 79 | int count = canvas.save(); 80 | 81 | 82 | if(shouldRedraw){ 83 | 84 | 85 | final Path reDrawPath = new Path(); 86 | cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, mMetrics); 87 | //// Log.e("TAG",Float.toString(cornerRadius)); 88 | // nowValue += cornerRadius /30; 89 | //18 200 90 | reDrawPath.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), cornerRadius, cornerRadius, Path.Direction.CW); 91 | canvas.clipPath(reDrawPath, Region.Op.REPLACE); 92 | 93 | canvas.clipPath(reDrawPath); 94 | super.dispatchDraw(canvas); 95 | canvas.restoreToCount(count); 96 | 97 | // if(nowValue < cornerRadius) 98 | // this.postInvalidateDelayed( 1000 / framesPerSecond); 99 | 100 | // Log.e("nowValue",Float.toString(nowValue)); 101 | } 102 | else{ 103 | 104 | 105 | final Path path = new Path(); 106 | 107 | if(nowValue >0){ 108 | cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, mMetrics); 109 | nowValue -= cornerRadius / 20; 110 | } 111 | else{ 112 | nowValue = 0; 113 | } 114 | path.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), Math.max(0,nowValue), Math.max(0,nowValue), Path.Direction.CW); 115 | canvas.clipPath(path, Region.Op.REPLACE); 116 | 117 | canvas.clipPath(path); 118 | super.dispatchDraw(canvas); 119 | canvas.restoreToCount(count); 120 | 121 | if(nowValue > 0) 122 | this.postInvalidateDelayed( 1000 / framesPerSecond); 123 | 124 | } 125 | 126 | 127 | } 128 | 129 | 130 | } -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.widget.Toast; 4 | 5 | 6 | /** 7 | * Created by lcodecore on 2017/2/28. 8 | */ 9 | 10 | public class ToastUtil { 11 | public static void show(String msg){ 12 | Toast.makeText(WaterfallApplication.appContext, msg, Toast.LENGTH_SHORT).show(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/VibratorUtil.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.app.Activity; 4 | import android.app.Service; 5 | import android.content.Context; 6 | import android.os.Vibrator; 7 | 8 | /** 9 | * Created by MartinRGB on 2017/9/5. 10 | */ 11 | 12 | public class VibratorUtil { 13 | 14 | /** 15 | * final Activity activity :调用该方法的Activity实例 16 | * long milliseconds :震动的时长,单位是毫秒 17 | * long[] pattern :自定义震动模式 。数组中数字的含义依次是[静止时长,震动时长,静止时长,震动时长。。。]时长的单位是毫秒 18 | * boolean isRepeat : 是否反复震动,如果是true,反复震动,如果是false,只震动一次 19 | */ 20 | 21 | public static void Vibrate(final Activity activity, long milliseconds) { 22 | Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE); 23 | vib.vibrate(milliseconds); 24 | } 25 | public static void Vibrate(final Activity activity, long[] pattern,boolean isRepeat) { 26 | Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE); 27 | vib.vibrate(pattern, isRepeat ? 1 : -1); 28 | } 29 | 30 | public static void Vibrate(final Context context, long milliseconds) { 31 | Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE); 32 | vib.vibrate(milliseconds); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/martinrgb/waterfalllayout/utils/WaterfallApplication.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.utils; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.util.Log; 7 | 8 | import com.github.moduth.blockcanary.BlockCanary; 9 | import com.squareup.leakcanary.LeakCanary; 10 | import com.squareup.leakcanary.RefWatcher; 11 | 12 | 13 | /** 14 | * Created by lcodecore on 2016/12/4. 15 | */ 16 | 17 | public class WaterfallApplication extends Application { 18 | 19 | public static Context appContext; 20 | 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | 25 | appContext = this; 26 | 27 | if (LeakCanary.isInAnalyzerProcess(this)) { 28 | // This process is dedicated to LeakCanary for heap analysis. 29 | // You should not init your app in this process. 30 | return; 31 | } 32 | LeakCanary.install(this); 33 | BlockCanary.install(this, new AppBlockCanaryContext()).start(); 34 | // Normal app init code... 35 | 36 | // Takt.stock(this) 37 | // .hide() 38 | // .listener(new Audience() { 39 | // @Override public void heartbeat(double fps) { 40 | // Log.d("Excellent!", fps + " fps"); 41 | // } 42 | // }) 43 | // .play(); 44 | 45 | 46 | setupLeakCanary(); 47 | } 48 | 49 | protected RefWatcher setupLeakCanary() { 50 | if (LeakCanary.isInAnalyzerProcess(this)) { 51 | return RefWatcher.DISABLED; 52 | } 53 | return LeakCanary.install(this); 54 | } 55 | 56 | @Override public void onTerminate() { 57 | //Takt.finish(); 58 | super.onTerminate(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/res/anim/scale_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/scale_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/as0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/as0.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/as1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/as1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/as2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/as2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/as3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/as3.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/as4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/as4.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/as5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/as5.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/background.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/closebtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/closebtn.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/closebtnp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/closebtnp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/icon0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/icon1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/icon2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/icon3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/icon3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/icon4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/icon4.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/icon5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/icon5.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/iconlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/iconlist.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/image0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/image0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/image0_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/image0_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/image1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/image2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/image3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/image4.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/image5.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/longpressmenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/longpressmenu.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/longpresstext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/longpresstext.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/shadow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/shadowlayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/shadowlayer.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/statsdark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/statsdark.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/statshome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/statshome.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/statswhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/statswhite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/tinttext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable-nodpi/tinttext.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/drawable/icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedge.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedgebig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 17 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 40 | 41 | 42 | 43 | 49 | 50 | 57 | 58 | 59 | 71 | 72 | 73 | 74 | 81 | 82 | 83 | 91 | 92 | 93 | 99 | 100 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 123 | 124 | 125 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 142 | 143 | 149 | 150 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /app/src/main/res/layout/backup/activity_logo.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/backup/backup/LogoActivity.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.backup; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.ImageView; 6 | 7 | import com.martinrgb.waterfalllayout.R; 8 | import com.zach.salman.springylib.SpringAnimationType; 9 | import com.zach.salman.springylib.SpringyAnimator; 10 | 11 | public class LogoActivity extends AppCompatActivity { 12 | 13 | ImageView logo; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_logo); 19 | 20 | logo = (ImageView) findViewById(R.id.logo); 21 | 22 | 23 | final SpringyAnimator scaleY = new SpringyAnimator(SpringAnimationType.SCALEY,5,3,0.5f,1); 24 | final SpringyAnimator rotate = new SpringyAnimator(SpringAnimationType.ROTATEY,5,3,180,0); 25 | rotate.setDelay(100); 26 | scaleY.setDelay(200); 27 | rotate.startSpring(logo); 28 | scaleY.startSpring(logo); 29 | 30 | 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/backup/backup/MediaActivity.java: -------------------------------------------------------------------------------- 1 | package com.martinrgb.waterfalllayout.backup; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.ImageView; 8 | 9 | import com.martinrgb.waterfalllayout.R; 10 | import com.zach.salman.springylib.SpringAnimationType; 11 | import com.zach.salman.springylib.SpringyAnimator; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class MediaActivity extends AppCompatActivity { 17 | 18 | private static final double TRANS_TENSION = 15; 19 | private static final double TRANS_FRACTION = 5; 20 | View 21 | image, 22 | gradient, 23 | layer1, 24 | layer2, 25 | title, 26 | heart, 27 | artist, 28 | forward, 29 | rewind, 30 | repeat, 31 | random, 32 | musicBar; 33 | ImageView play; 34 | private List views = new ArrayList<>(); 35 | 36 | private static final int DELAY = 100; 37 | private boolean isPlay =true; 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setContentView(R.layout.media_layout); 43 | init(); 44 | } 45 | 46 | 47 | 48 | 49 | private void init(){ 50 | image = findViewById(R.id.image); 51 | gradient = findViewById(R.id.gradient); 52 | title = findViewById(R.id.title); 53 | artist = findViewById(R.id.artist); 54 | heart = findViewById(R.id.heart); 55 | layer1 = findViewById(R.id.layer1); 56 | layer2 = findViewById(R.id.layer2); 57 | play = (ImageView) findViewById(R.id.play); 58 | forward = findViewById(R.id.forward); 59 | rewind = findViewById(R.id.rewind); 60 | repeat = findViewById(R.id.repeat); 61 | random = findViewById(R.id.random); 62 | musicBar = findViewById(R.id.music_bar); 63 | 64 | views.add(layer1); 65 | views.add(layer2); 66 | views.add(play); 67 | 68 | setTranslate(image,DELAY*2); 69 | setTranslate(gradient,DELAY*3); 70 | setTranslate(title,DELAY*7); 71 | setTranslate(artist,DELAY*8); 72 | setTranslate(forward,DELAY*11); 73 | setTranslate(rewind,DELAY*11); 74 | 75 | setScale(layer1,DELAY*10); 76 | setScale(layer2,DELAY*11); 77 | setScale(play,DELAY*12); 78 | setScale(repeat,DELAY*16); 79 | setScale(random,DELAY*17); 80 | setScale(heart,DELAY*18); 81 | setScale(musicBar,DELAY*12); 82 | 83 | heart.setOnClickListener(new View.OnClickListener() { 84 | @Override 85 | public void onClick(View view) { 86 | animateView(view); 87 | } 88 | }); 89 | 90 | repeat.setOnClickListener(new View.OnClickListener() { 91 | @Override 92 | public void onClick(View view) { 93 | animateView(view); 94 | } 95 | }); 96 | 97 | random.setOnClickListener(new View.OnClickListener() { 98 | @Override 99 | public void onClick(View view) { 100 | animateView(view); 101 | } 102 | }); 103 | 104 | 105 | play.setOnClickListener(new View.OnClickListener() { 106 | @Override 107 | public void onClick(View view) { 108 | 109 | playAnimate(); 110 | } 111 | }); 112 | 113 | } 114 | 115 | 116 | private void animateView(View view){ 117 | SpringyAnimator springHelper = new SpringyAnimator(SpringAnimationType.SCALEXY,100,4,0,1); 118 | springHelper.startSpring(view); 119 | } 120 | 121 | 122 | private void playAnimate(){ 123 | final SpringyAnimator springHelper = new SpringyAnimator(SpringAnimationType.SCALEXY,100,3,0.5f,1); 124 | for (int i = 0; i 2 | 7 | 8 | 14 | 15 | 21 | 22 | 34 | 35 | 47 | 48 | 58 | 59 | 63 | 64 | 74 | 75 | 85 | 86 | 94 | 102 | 112 | 121 | 131 | 132 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 22 | 23 | 29 | 30 | 39 | 40 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 66 | 67 | 72 | 73 | 74 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #717171 4 | #A2A2A2 5 | #464646 6 | #F4C828 7 | 8 | #78ffffff 9 | #1400bcd4 10 | #14000000 11 | #6400bcd4 12 | 13 | @android:color/holo_purple 14 | #ccaa66cc 15 | @android:color/holo_purple 16 | #EEEEEE 17 | #50000000 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16dp 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WaterfallLayout 3 | Waterfall 4 | LogoActivity 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 18 | 19 | 27 | 28 | 31 | 32 | -------------------------------------------------------------------------------- /app/version.properties: -------------------------------------------------------------------------------- 1 | VERSION_CODE= 0056 -------------------------------------------------------------------------------- /art/art_real.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/art/art_real.gif -------------------------------------------------------------------------------- /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 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.1.3' 10 | 11 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' 12 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | jcenter() 22 | // maven { 23 | // url 'https://maven.google.com' 24 | // // Alternative URL is 'https://dl.google.com/dl/android/maven2/' 25 | // } 26 | google() 27 | } 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinRGB/MI_Style_Multitask_Prototype/f5391b9f792979c837875e0ba8e06ca3d69fe6f7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 06 11:48:50 CST 2018 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-4.4-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /liboverscroll/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /liboverscroll/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '27.0.3' 6 | 7 | defaultConfig { 8 | minSdkVersion 17 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation 'com.android.support:recyclerview-v7:25.1.0' 23 | 24 | testImplementation 'junit:junit:4.12' 25 | testImplementation "org.mockito:mockito-core:1.9.5" 26 | testImplementation "org.robolectric:robolectric:3.0" 27 | } 28 | 29 | 30 | // Running from Gradle tab in IDE would create liboverscroll/build/lib/liboverscroll-sources.jar 31 | task sourcesJar(type: Jar) { 32 | from android.sourceSets.main.java.srcDirs 33 | classifier = 'sources' 34 | } 35 | 36 | task javadoc(type: Javadoc) { 37 | failOnError false 38 | source = android.sourceSets.main.java.srcDirs 39 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 40 | } 41 | 42 | // Running from Gradle tab in IDE would create liboverscroll/build/lib/liboverscroll-javadoc.jar 43 | task javadocJar(type: Jar, dependsOn: javadoc) { 44 | classifier = 'javadoc' 45 | from javadoc.destinationDir 46 | } 47 | 48 | artifacts { 49 | archives javadocJar 50 | archives sourcesJar 51 | } 52 | -------------------------------------------------------------------------------- /liboverscroll/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecorator.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | import android.view.MotionEvent; 4 | import android.view.View; 5 | 6 | import me.everything.android.ui.overscroll.adapters.IOverScrollDecoratorAdapter; 7 | 8 | /** 9 | * A concrete implementation of {@link OverScrollBounceEffectDecoratorBase} for a horizontal orientation. 10 | * 11 | * @author amit 12 | */ 13 | public class HorizontalOverScrollBounceEffectDecorator extends OverScrollBounceEffectDecoratorBase { 14 | 15 | protected static class MotionAttributesHorizontal extends MotionAttributes { 16 | 17 | public boolean init(View view, MotionEvent event) { 18 | 19 | // We must have history available to calc the dx. Normally it's there - if it isn't temporarily, 20 | // we declare the event 'invalid' and expect it in consequent events. 21 | if (event.getHistorySize() == 0) { 22 | return false; 23 | } 24 | 25 | // Allow for counter-orientation-direction operations (e.g. item swiping) to run fluently. 26 | final float dy = event.getY(0) - event.getHistoricalY(0, 0); 27 | final float dx = event.getX(0) - event.getHistoricalX(0, 0); 28 | if (Math.abs(dx) < Math.abs(dy)) { 29 | return false; 30 | } 31 | 32 | mAbsOffset = view.getTranslationX(); 33 | mDeltaOffset = dx; 34 | mDir = mDeltaOffset > 0; 35 | 36 | return true; 37 | } 38 | } 39 | 40 | protected static class AnimationAttributesHorizontal extends AnimationAttributes { 41 | 42 | public AnimationAttributesHorizontal() { 43 | mProperty = View.TRANSLATION_X; 44 | } 45 | 46 | @Override 47 | protected void init(View view) { 48 | mAbsOffset = view.getTranslationX(); 49 | mMaxOffset = view.getWidth(); 50 | } 51 | } 52 | 53 | /** 54 | * C'tor, creating the effect with default arguments: 55 | *
Touch-drag ratio in 'forward' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD. 56 | *
Touch-drag ratio in 'backwards' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK. 57 | *
Deceleration factor (for the bounce-back effect) will be set to DEFAULT_DECELERATE_FACTOR. 58 | * 59 | * @param viewAdapter The view's encapsulation. 60 | */ 61 | public HorizontalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter) { 62 | this(viewAdapter, DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD, DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK, DEFAULT_DECELERATE_FACTOR); 63 | } 64 | 65 | /** 66 | * C'tor, creating the effect with explicit arguments. 67 | * @param viewAdapter The view's encapsulation. 68 | * @param touchDragRatioFwd Ratio of touch distance to actual drag distance when in 'forward' direction. 69 | * @param touchDragRatioBck Ratio of touch distance to actual drag distance when in 'backward' 70 | * direction (opposite to initial one). 71 | * @param decelerateFactor Deceleration factor used when decelerating the motion to create the 72 | * bounce-back effect. 73 | */ 74 | public HorizontalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter, 75 | float touchDragRatioFwd, float touchDragRatioBck, float decelerateFactor) { 76 | super(viewAdapter, decelerateFactor, touchDragRatioFwd, touchDragRatioBck); 77 | } 78 | 79 | @Override 80 | protected MotionAttributes createMotionAttributes() { 81 | return new MotionAttributesHorizontal(); 82 | } 83 | 84 | @Override 85 | protected AnimationAttributes createAnimationAttributes() { 86 | return new AnimationAttributesHorizontal(); 87 | } 88 | 89 | @Override 90 | protected void translateView(View view, float offset) { 91 | view.setTranslationX(offset); 92 | } 93 | 94 | @Override 95 | protected void translateViewAndEvent(View view, float offset, MotionEvent event) { 96 | view.setTranslationX(offset); 97 | event.offsetLocation(offset - event.getX(0), 0f); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollDecor.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * @author amit 7 | */ 8 | public interface IOverScrollDecor { 9 | View getView(); 10 | 11 | void setOverScrollStateListener(IOverScrollStateListener listener); 12 | void setOverScrollUpdateListener(IOverScrollUpdateListener listener); 13 | 14 | /** 15 | * Get the current decorator's runtime state, i.e. one of the values specified by {@link IOverScrollState}. 16 | * @return The state. 17 | */ 18 | int getCurrentState(); 19 | 20 | /** 21 | * Detach the decorator from its associated view, thus disabling it entirely. 22 | * 23 | *

It is best to call this only when over-scroll isn't currently in-effect - i.e. verify that 24 | * getCurrentState()==IOverScrollState.STATE_IDLE as a precondition, or otherwise 25 | * use a state listener previously installed using 26 | * {@link #setOverScrollStateListener(IOverScrollStateListener)}.

27 | * 28 | *

Note: Upon detachment completion, the view in question will return to the default 29 | * Android over-scroll configuration (i.e. {@link View.OVER_SCROLL_ALWAYS} mode). This can be 30 | * overridden by calling View.setOverScrollMode(mode) immediately thereafter.

31 | */ 32 | void detach(); 33 | } 34 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollState.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | /** 4 | * @author amit 5 | */ 6 | public interface IOverScrollState { 7 | 8 | /** No over-scroll is in-effect. */ 9 | int STATE_IDLE = 0; 10 | 11 | /** User is actively touch-dragging, thus enabling over-scroll at the view's start side. */ 12 | int STATE_DRAG_START_SIDE = 1; 13 | 14 | /** User is actively touch-dragging, thus enabling over-scroll at the view's end side. */ 15 | int STATE_DRAG_END_SIDE = 2; 16 | 17 | /** User has released their touch, thus throwing the view back into place via bounce-back animation. */ 18 | int STATE_BOUNCE_BACK = 3; 19 | } 20 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollStateListener.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | /** 4 | * A callback-listener enabling over-scroll effect clients to be notified of effect state transitions. 5 | *
Invoked whenever state is transitioned onto one of {@link IOverScrollState#STATE_IDLE}, 6 | * {@link IOverScrollState#STATE_DRAG_START_SIDE}, {@link IOverScrollState#STATE_DRAG_END_SIDE} 7 | * or {@link IOverScrollState#STATE_BOUNCE_BACK}. 8 | * 9 | * @author amit 10 | * 11 | * @see IOverScrollUpdateListener 12 | */ 13 | public interface IOverScrollStateListener { 14 | 15 | /** 16 | * The invoked callback. 17 | * 18 | * @param decor The associated over-scroll 'decorator'. 19 | * @param oldState The old over-scroll state; ID's specified by {@link IOverScrollState}, e.g. 20 | * {@link IOverScrollState#STATE_IDLE}. 21 | * @param newState The new over-scroll state; ID's specified by {@link IOverScrollState}, 22 | * e.g. {@link IOverScrollState#STATE_IDLE}. 23 | */ 24 | void onOverScrollStateChange(IOverScrollDecor decor, int oldState, int newState); 25 | } 26 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollUpdateListener.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | /** 4 | * A callback-listener enabling over-scroll effect clients to subscribe to real-time updates 5 | * of over-scrolling intensity, provided as the view-translation offset from pre-scroll position. 6 | * 7 | * @author amit 8 | * 9 | * @see IOverScrollStateListener 10 | */ 11 | public interface IOverScrollUpdateListener { 12 | 13 | /** 14 | * The invoked callback. 15 | * 16 | * @param decor The associated over-scroll 'decorator'. 17 | * @param state One of: {@link IOverScrollState#STATE_IDLE}, {@link IOverScrollState#STATE_DRAG_START_SIDE}, 18 | * {@link IOverScrollState#STATE_DRAG_START_SIDE} or {@link IOverScrollState#STATE_BOUNCE_BACK}. 19 | * @param offset The currently visible offset created due to over-scroll. 20 | */ 21 | void onOverScrollUpdate(IOverScrollDecor decor, int state, float offset); 22 | } 23 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/ListenerStubs.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | /** 4 | * @author amit 5 | */ 6 | public interface ListenerStubs { 7 | 8 | class OverScrollStateListenerStub implements IOverScrollStateListener { 9 | @Override 10 | public void onOverScrollStateChange(IOverScrollDecor decor, int oldState, int newState) { } 11 | } 12 | 13 | class OverScrollUpdateListenerStub implements IOverScrollUpdateListener { 14 | @Override 15 | public void onOverScrollUpdate(IOverScrollDecor decor, int state, float offset) { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollDecoratorHelper.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | import android.support.v4.view.ViewPager; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.support.v7.widget.StaggeredGridLayoutManager; 8 | import android.view.View; 9 | import android.widget.GridView; 10 | import android.widget.HorizontalScrollView; 11 | import android.widget.ListView; 12 | import android.widget.ScrollView; 13 | 14 | import me.everything.android.ui.overscroll.adapters.AbsListViewOverScrollDecorAdapter; 15 | import me.everything.android.ui.overscroll.adapters.HorizontalScrollViewOverScrollDecorAdapter; 16 | import me.everything.android.ui.overscroll.adapters.RecyclerViewOverScrollDecorAdapter; 17 | import me.everything.android.ui.overscroll.adapters.ScrollViewOverScrollDecorAdapter; 18 | import me.everything.android.ui.overscroll.adapters.StaticOverScrollDecorAdapter; 19 | import me.everything.android.ui.overscroll.adapters.ViewPagerOverScrollDecorAdapter; 20 | 21 | /** 22 | * @author amit 23 | */ 24 | public class OverScrollDecoratorHelper { 25 | 26 | public static final int ORIENTATION_VERTICAL = 0; 27 | public static final int ORIENTATION_HORIZONTAL = 1; 28 | 29 | /** 30 | * Set up the over-scroll effect over a specified {@link RecyclerView} view. 31 | *
Only recycler-views using native Android layout managers (i.e. {@link LinearLayoutManager}, 32 | * {@link GridLayoutManager} and {@link StaggeredGridLayoutManager}) are currently supported 33 | * by this convenience method. 34 | * 35 | * @param recyclerView The view. 36 | * @param orientation Either {@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL}. 37 | * 38 | * @return The over-scroll effect 'decorator', enabling further effect configuration. 39 | */ 40 | public static IOverScrollDecor setUpOverScroll(RecyclerView recyclerView, int orientation) { 41 | switch (orientation) { 42 | case ORIENTATION_HORIZONTAL: 43 | return new HorizontalOverScrollBounceEffectDecorator(new RecyclerViewOverScrollDecorAdapter(recyclerView)); 44 | case ORIENTATION_VERTICAL: 45 | return new VerticalOverScrollBounceEffectDecorator(new RecyclerViewOverScrollDecorAdapter(recyclerView)); 46 | default: 47 | throw new IllegalArgumentException("orientation"); 48 | } 49 | } 50 | 51 | public static IOverScrollDecor setUpOverScroll(ListView listView) { 52 | return new VerticalOverScrollBounceEffectDecorator(new AbsListViewOverScrollDecorAdapter(listView)); 53 | } 54 | 55 | public static IOverScrollDecor setUpOverScroll(GridView gridView) { 56 | return new VerticalOverScrollBounceEffectDecorator(new AbsListViewOverScrollDecorAdapter(gridView)); 57 | } 58 | 59 | public static IOverScrollDecor setUpOverScroll(ScrollView scrollView) { 60 | return new VerticalOverScrollBounceEffectDecorator(new ScrollViewOverScrollDecorAdapter(scrollView)); 61 | } 62 | 63 | public static IOverScrollDecor setUpOverScroll(HorizontalScrollView scrollView) { 64 | return new HorizontalOverScrollBounceEffectDecorator(new HorizontalScrollViewOverScrollDecorAdapter(scrollView)); 65 | } 66 | 67 | /** 68 | * Set up the over-scroll over a generic view, assumed to always be over-scroll ready (e.g. 69 | * a plain text field, image view). 70 | * 71 | * @param view The view. 72 | * @param orientation One of {@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL}. 73 | * 74 | * @return The over-scroll effect 'decorator', enabling further effect configuration. 75 | */ 76 | public static IOverScrollDecor setUpStaticOverScroll(View view, int orientation) { 77 | switch (orientation) { 78 | case ORIENTATION_HORIZONTAL: 79 | return new HorizontalOverScrollBounceEffectDecorator(new StaticOverScrollDecorAdapter(view)); 80 | 81 | case ORIENTATION_VERTICAL: 82 | return new VerticalOverScrollBounceEffectDecorator(new StaticOverScrollDecorAdapter(view)); 83 | 84 | default: 85 | throw new IllegalArgumentException("orientation"); 86 | } 87 | } 88 | 89 | public static IOverScrollDecor setUpOverScroll(ViewPager viewPager) { 90 | return new HorizontalOverScrollBounceEffectDecorator(new ViewPagerOverScrollDecorAdapter(viewPager)); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecorator.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll; 2 | 3 | import android.view.MotionEvent; 4 | import android.view.View; 5 | 6 | import me.everything.android.ui.overscroll.adapters.IOverScrollDecoratorAdapter; 7 | 8 | /** 9 | * A concrete implementation of {@link OverScrollBounceEffectDecoratorBase} for a vertical orientation. 10 | * 11 | * @author amit 12 | */ 13 | public class VerticalOverScrollBounceEffectDecorator extends OverScrollBounceEffectDecoratorBase { 14 | 15 | protected static class MotionAttributesVertical extends MotionAttributes { 16 | 17 | public boolean init(View view, MotionEvent event) { 18 | 19 | // We must have history available to calc the dx. Normally it's there - if it isn't temporarily, 20 | // we declare the event 'invalid' and expect it in consequent events. 21 | if (event.getHistorySize() == 0) { 22 | return false; 23 | } 24 | 25 | // Allow for counter-orientation-direction operations (e.g. item swiping) to run fluently. 26 | final float dy = event.getY(0) - event.getHistoricalY(0, 0); 27 | final float dx = event.getX(0) - event.getHistoricalX(0, 0); 28 | if (Math.abs(dx) > Math.abs(dy)) { 29 | return false; 30 | } 31 | 32 | mAbsOffset = view.getTranslationY(); 33 | mDeltaOffset = dy; 34 | mDir = mDeltaOffset > 0; 35 | 36 | return true; 37 | } 38 | } 39 | 40 | protected static class AnimationAttributesVertical extends AnimationAttributes { 41 | 42 | public AnimationAttributesVertical() { 43 | mProperty = View.TRANSLATION_Y; 44 | } 45 | 46 | @Override 47 | protected void init(View view) { 48 | mAbsOffset = view.getTranslationY(); 49 | mMaxOffset = view.getHeight(); 50 | } 51 | } 52 | 53 | /** 54 | * C'tor, creating the effect with default arguments: 55 | *
Touch-drag ratio in 'forward' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD. 56 | *
Touch-drag ratio in 'backwards' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK. 57 | *
Deceleration factor (for the bounce-back effect) will be set to DEFAULT_DECELERATE_FACTOR. 58 | * 59 | * @param viewAdapter The view's encapsulation. 60 | */ 61 | public VerticalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter) { 62 | this(viewAdapter, DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD, DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK, DEFAULT_DECELERATE_FACTOR); 63 | } 64 | 65 | /** 66 | * C'tor, creating the effect with explicit arguments. 67 | * @param viewAdapter The view's encapsulation. 68 | * @param touchDragRatioFwd Ratio of touch distance to actual drag distance when in 'forward' direction. 69 | * @param touchDragRatioBck Ratio of touch distance to actual drag distance when in 'backward' 70 | * direction (opposite to initial one). 71 | * @param decelerateFactor Deceleration factor used when decelerating the motion to create the 72 | * bounce-back effect. 73 | */ 74 | public VerticalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter, 75 | float touchDragRatioFwd, float touchDragRatioBck, float decelerateFactor) { 76 | super(viewAdapter, decelerateFactor, touchDragRatioFwd, touchDragRatioBck); 77 | } 78 | 79 | @Override 80 | protected MotionAttributes createMotionAttributes() { 81 | return new MotionAttributesVertical(); 82 | } 83 | 84 | @Override 85 | protected AnimationAttributes createAnimationAttributes() { 86 | return new AnimationAttributesVertical(); 87 | } 88 | 89 | @Override 90 | protected void translateView(View view, float offset) { 91 | view.setTranslationY(offset); 92 | } 93 | 94 | @Override 95 | protected void translateViewAndEvent(View view, float offset, MotionEvent event) { 96 | view.setTranslationY(offset); 97 | event.offsetLocation(offset - event.getY(0), 0f); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/AbsListViewOverScrollDecorAdapter.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll.adapters; 2 | 3 | import android.view.View; 4 | import android.widget.AbsListView; 5 | 6 | import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; 7 | import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; 8 | 9 | /** 10 | * An adapter to enable over-scrolling over object of {@link AbsListView}, namely {@link 11 | * android.widget.ListView} and it's extensions, and {@link android.widget.GridView}. 12 | * 13 | * @author amit 14 | * 15 | * @see HorizontalOverScrollBounceEffectDecorator 16 | * @see VerticalOverScrollBounceEffectDecorator 17 | */ 18 | public class AbsListViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { 19 | 20 | protected final AbsListView mView; 21 | 22 | public AbsListViewOverScrollDecorAdapter(AbsListView view) { 23 | mView = view; 24 | } 25 | 26 | @Override 27 | public View getView() { 28 | return mView; 29 | } 30 | 31 | @Override 32 | public boolean isInAbsoluteStart() { 33 | return mView.getChildCount() > 0 && !canScrollListUp(); 34 | } 35 | 36 | @Override 37 | public boolean isInAbsoluteEnd() { 38 | return mView.getChildCount() > 0 && !canScrollListDown(); 39 | } 40 | 41 | public boolean canScrollListUp() { 42 | // Ported from AbsListView#canScrollList() which isn't compatible to all API levels 43 | final int firstTop = mView.getChildAt(0).getTop(); 44 | final int firstPosition = mView.getFirstVisiblePosition(); 45 | return firstPosition > 0 || firstTop < mView.getListPaddingTop(); 46 | } 47 | 48 | public boolean canScrollListDown() { 49 | // Ported from AbsListView#canScrollList() which isn't compatible to all API levels 50 | final int childCount = mView.getChildCount(); 51 | final int itemsCount = mView.getCount(); 52 | final int firstPosition = mView.getFirstVisiblePosition(); 53 | final int lastPosition = firstPosition + childCount; 54 | final int lastBottom = mView.getChildAt(childCount - 1).getBottom(); 55 | return lastPosition < itemsCount || lastBottom > mView.getHeight() - mView.getListPaddingBottom(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/HorizontalScrollViewOverScrollDecorAdapter.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll.adapters; 2 | 3 | import android.view.View; 4 | import android.widget.HorizontalScrollView; 5 | 6 | import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; 7 | import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; 8 | 9 | /** 10 | * An adapter that enables over-scrolling support over a {@link HorizontalScrollView}. 11 | *
Seeing that {@link HorizontalScrollView} only supports horizontal scrolling, this adapter 12 | * should only be used with a {@link HorizontalOverScrollBounceEffectDecorator}. 13 | * 14 | * @author amit 15 | * 16 | * @see HorizontalOverScrollBounceEffectDecorator 17 | * @see VerticalOverScrollBounceEffectDecorator 18 | */ 19 | public class HorizontalScrollViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { 20 | 21 | protected final HorizontalScrollView mView; 22 | 23 | public HorizontalScrollViewOverScrollDecorAdapter(HorizontalScrollView view) { 24 | mView = view; 25 | } 26 | 27 | @Override 28 | public View getView() { 29 | return mView; 30 | } 31 | 32 | @Override 33 | public boolean isInAbsoluteStart() { 34 | return !mView.canScrollHorizontally(-1); 35 | } 36 | 37 | @Override 38 | public boolean isInAbsoluteEnd() { 39 | return !mView.canScrollHorizontally(1); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/IOverScrollDecoratorAdapter.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll.adapters; 2 | 3 | import android.view.View; 4 | 5 | import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; 6 | 7 | /** 8 | * @author amitd 9 | * 10 | * @see HorizontalOverScrollBounceEffectDecorator 11 | */ 12 | public interface IOverScrollDecoratorAdapter { 13 | 14 | View getView(); 15 | 16 | /** 17 | * Is view in it's absolute start position - such that a negative over-scroll can potentially 18 | * be initiated. For example, in list-views, this is synonymous with the first item being 19 | * fully visible. 20 | * 21 | * @return Whether in absolute start position. 22 | */ 23 | boolean isInAbsoluteStart(); 24 | 25 | /** 26 | * Is view in it's absolute end position - such that an over-scroll can potentially 27 | * be initiated. For example, in list-views, this is synonymous with the last item being 28 | * fully visible. 29 | * 30 | * @return Whether in absolute end position. 31 | */ 32 | boolean isInAbsoluteEnd(); 33 | } 34 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/RecyclerViewOverScrollDecorAdapter.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll.adapters; 2 | 3 | import android.graphics.Canvas; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.support.v7.widget.StaggeredGridLayoutManager; 7 | import android.support.v7.widget.helper.ItemTouchHelper; 8 | import android.view.View; 9 | 10 | import java.util.List; 11 | 12 | import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; 13 | import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; 14 | 15 | /** 16 | * @author amitd 17 | * 18 | * @see HorizontalOverScrollBounceEffectDecorator 19 | * @see VerticalOverScrollBounceEffectDecorator 20 | */ 21 | public class RecyclerViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { 22 | 23 | /** 24 | * A delegation of the adapter implementation of this view that should provide the processing 25 | * of {@link #isInAbsoluteStart()} and {@link #isInAbsoluteEnd()}. Essentially needed simply 26 | * because the implementation depends on the layout manager implementation being used. 27 | */ 28 | protected interface Impl { 29 | boolean isInAbsoluteStart(); 30 | boolean isInAbsoluteEnd(); 31 | } 32 | 33 | protected final RecyclerView mRecyclerView; 34 | protected final Impl mImpl; 35 | 36 | protected boolean mIsItemTouchInEffect = false; 37 | 38 | public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView) { 39 | 40 | mRecyclerView = recyclerView; 41 | 42 | final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 43 | if (layoutManager instanceof LinearLayoutManager || 44 | layoutManager instanceof StaggeredGridLayoutManager) 45 | { 46 | final int orientation = 47 | (layoutManager instanceof LinearLayoutManager 48 | ? ((LinearLayoutManager) layoutManager).getOrientation() 49 | : ((StaggeredGridLayoutManager) layoutManager).getOrientation()); 50 | 51 | if (orientation == LinearLayoutManager.HORIZONTAL) { 52 | mImpl = new ImplHorizLayout(); 53 | } else { 54 | mImpl = new ImplVerticalLayout(); 55 | } 56 | } 57 | else 58 | { 59 | throw new IllegalArgumentException("Recycler views with custom layout managers are not supported by this adapter out of the box." + 60 | "Try implementing and providing an explicit 'impl' parameter to the other c'tors, or otherwise create a custom adapter subclass of your own."); 61 | } 62 | } 63 | 64 | public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView, Impl impl) { 65 | mRecyclerView = recyclerView; 66 | mImpl = impl; 67 | } 68 | 69 | public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView, ItemTouchHelper.Callback itemTouchHelperCallback) { 70 | this(recyclerView); 71 | setUpTouchHelperCallback(itemTouchHelperCallback); 72 | } 73 | 74 | public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView, Impl impl, ItemTouchHelper.Callback itemTouchHelperCallback) { 75 | this(recyclerView, impl); 76 | setUpTouchHelperCallback(itemTouchHelperCallback); 77 | } 78 | 79 | protected void setUpTouchHelperCallback(final ItemTouchHelper.Callback itemTouchHelperCallback) { 80 | new ItemTouchHelper(new ItemTouchHelperCallbackWrapper(itemTouchHelperCallback) { 81 | @Override 82 | public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { 83 | mIsItemTouchInEffect = actionState != 0; 84 | super.onSelectedChanged(viewHolder, actionState); 85 | } 86 | }).attachToRecyclerView(mRecyclerView); 87 | } 88 | 89 | @Override 90 | public View getView() { 91 | return mRecyclerView; 92 | } 93 | 94 | @Override 95 | public boolean isInAbsoluteStart() { 96 | return !mIsItemTouchInEffect && mImpl.isInAbsoluteStart(); 97 | } 98 | 99 | @Override 100 | public boolean isInAbsoluteEnd() { 101 | return !mIsItemTouchInEffect && mImpl.isInAbsoluteEnd(); 102 | } 103 | 104 | protected class ImplHorizLayout implements Impl { 105 | 106 | @Override 107 | public boolean isInAbsoluteStart() { 108 | return !mRecyclerView.canScrollHorizontally(-1); 109 | } 110 | 111 | @Override 112 | public boolean isInAbsoluteEnd() { 113 | return !mRecyclerView.canScrollHorizontally(1); 114 | } 115 | } 116 | 117 | protected class ImplVerticalLayout implements Impl { 118 | 119 | @Override 120 | public boolean isInAbsoluteStart() { 121 | return !mRecyclerView.canScrollVertically(-1); 122 | } 123 | 124 | @Override 125 | public boolean isInAbsoluteEnd() { 126 | return !mRecyclerView.canScrollVertically(1); 127 | } 128 | } 129 | 130 | private static class ItemTouchHelperCallbackWrapper extends ItemTouchHelper.Callback { 131 | 132 | final ItemTouchHelper.Callback mCallback; 133 | 134 | private ItemTouchHelperCallbackWrapper(ItemTouchHelper.Callback callback) { 135 | mCallback = callback; 136 | } 137 | 138 | @Override 139 | public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { 140 | return mCallback.getMovementFlags(recyclerView, viewHolder); 141 | } 142 | 143 | @Override 144 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 145 | return mCallback.onMove(recyclerView, viewHolder, target); 146 | } 147 | 148 | @Override 149 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { 150 | mCallback.onSwiped(viewHolder, direction); 151 | } 152 | 153 | @Override 154 | public int convertToAbsoluteDirection(int flags, int layoutDirection) { 155 | return mCallback.convertToAbsoluteDirection(flags, layoutDirection); 156 | } 157 | 158 | @Override 159 | public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) { 160 | return mCallback.canDropOver(recyclerView, current, target); 161 | } 162 | 163 | @Override 164 | public boolean isLongPressDragEnabled() { 165 | return mCallback.isLongPressDragEnabled(); 166 | } 167 | 168 | @Override 169 | public boolean isItemViewSwipeEnabled() { 170 | return mCallback.isItemViewSwipeEnabled(); 171 | } 172 | 173 | @Override 174 | public int getBoundingBoxMargin() { 175 | return mCallback.getBoundingBoxMargin(); 176 | } 177 | 178 | @Override 179 | public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) { 180 | return mCallback.getSwipeThreshold(viewHolder); 181 | } 182 | 183 | @Override 184 | public float getMoveThreshold(RecyclerView.ViewHolder viewHolder) { 185 | return mCallback.getMoveThreshold(viewHolder); 186 | } 187 | 188 | @Override 189 | public RecyclerView.ViewHolder chooseDropTarget(RecyclerView.ViewHolder selected, List dropTargets, int curX, int curY) { 190 | return mCallback.chooseDropTarget(selected, dropTargets, curX, curY); 191 | } 192 | 193 | @Override 194 | public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { 195 | mCallback.onSelectedChanged(viewHolder, actionState); 196 | } 197 | 198 | @Override 199 | public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) { 200 | mCallback.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y); 201 | } 202 | 203 | @Override 204 | public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { 205 | mCallback.clearView(recyclerView, viewHolder); 206 | } 207 | 208 | @Override 209 | public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { 210 | mCallback.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); 211 | } 212 | 213 | @Override 214 | public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { 215 | mCallback.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); 216 | } 217 | 218 | @Override 219 | public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) { 220 | return mCallback.getAnimationDuration(recyclerView, animationType, animateDx, animateDy); 221 | } 222 | 223 | @Override 224 | public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize, int viewSizeOutOfBounds, int totalSize, long msSinceStartScroll) { 225 | return mCallback.interpolateOutOfBoundsScroll(recyclerView, viewSize, viewSizeOutOfBounds, totalSize, msSinceStartScroll); 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/ScrollViewOverScrollDecorAdapter.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll.adapters; 2 | 3 | import android.view.View; 4 | import android.widget.ScrollView; 5 | 6 | import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; 7 | import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; 8 | 9 | /** 10 | * An adapter that enables over-scrolling over a {@link ScrollView}. 11 | *
Seeing that {@link ScrollView} only supports vertical scrolling, this adapter 12 | * should only be used with a {@link VerticalOverScrollBounceEffectDecorator}. For horizontal 13 | * over-scrolling, use {@link HorizontalScrollViewOverScrollDecorAdapter} in conjunction with 14 | * a {@link android.widget.HorizontalScrollView}. 15 | * 16 | * @author amit 17 | * 18 | * @see HorizontalOverScrollBounceEffectDecorator 19 | * @see VerticalOverScrollBounceEffectDecorator 20 | */ 21 | public class ScrollViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { 22 | 23 | protected final ScrollView mView; 24 | 25 | public ScrollViewOverScrollDecorAdapter(ScrollView view) { 26 | mView = view; 27 | } 28 | 29 | @Override 30 | public View getView() { 31 | return mView; 32 | } 33 | 34 | @Override 35 | public boolean isInAbsoluteStart() { 36 | return !mView.canScrollVertically(-1); 37 | } 38 | 39 | @Override 40 | public boolean isInAbsoluteEnd() { 41 | return !mView.canScrollVertically(1); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/StaticOverScrollDecorAdapter.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll.adapters; 2 | 3 | import android.view.View; 4 | 5 | import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; 6 | import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; 7 | 8 | /** 9 | * A static adapter for views that are ALWAYS over-scroll-able (e.g. image view). 10 | * 11 | * @author amit 12 | * 13 | * @see HorizontalOverScrollBounceEffectDecorator 14 | * @see VerticalOverScrollBounceEffectDecorator 15 | */ 16 | public class StaticOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { 17 | 18 | protected final View mView; 19 | 20 | public StaticOverScrollDecorAdapter(View view) { 21 | mView = view; 22 | } 23 | 24 | @Override 25 | public View getView() { 26 | return mView; 27 | } 28 | 29 | @Override 30 | public boolean isInAbsoluteStart() { 31 | return true; 32 | } 33 | 34 | @Override 35 | public boolean isInAbsoluteEnd() { 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/ViewPagerOverScrollDecorAdapter.java: -------------------------------------------------------------------------------- 1 | package me.everything.android.ui.overscroll.adapters; 2 | 3 | import android.support.v4.view.ViewPager; 4 | import android.view.View; 5 | 6 | import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; 7 | 8 | /** 9 | * Created by Bruce too 10 | * Enhance by amit 11 | * On 2016/6/16 12 | * At 14:51 13 | * An adapter to enable over-scrolling over object of {@link ViewPager} 14 | * 15 | * @see HorizontalOverScrollBounceEffectDecorator 16 | */ 17 | public class ViewPagerOverScrollDecorAdapter implements IOverScrollDecoratorAdapter, ViewPager.OnPageChangeListener { 18 | 19 | protected final ViewPager mViewPager; 20 | 21 | protected int mLastPagerPosition = 0; 22 | protected float mLastPagerScrollOffset; 23 | 24 | public ViewPagerOverScrollDecorAdapter(ViewPager viewPager) { 25 | this.mViewPager = viewPager; 26 | 27 | mViewPager.addOnPageChangeListener(this); 28 | 29 | mLastPagerPosition = mViewPager.getCurrentItem(); 30 | mLastPagerScrollOffset = 0f; 31 | } 32 | 33 | @Override 34 | public View getView() { 35 | return mViewPager; 36 | } 37 | 38 | @Override 39 | public boolean isInAbsoluteStart() { 40 | 41 | return mLastPagerPosition == 0 && 42 | mLastPagerScrollOffset == 0f; 43 | } 44 | 45 | @Override 46 | public boolean isInAbsoluteEnd() { 47 | 48 | return mLastPagerPosition == mViewPager.getAdapter().getCount()-1 && 49 | mLastPagerScrollOffset == 0f; 50 | } 51 | 52 | @Override 53 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 54 | mLastPagerPosition = position; 55 | mLastPagerScrollOffset = positionOffset; 56 | } 57 | 58 | @Override 59 | public void onPageSelected(int position) { 60 | 61 | } 62 | 63 | @Override 64 | public void onPageScrollStateChanged(int state) { 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /recyclerviewhelper/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /recyclerviewhelper/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Jun 29 09:47:32 PDT 2017 2 | connection.project.dir=.. 3 | -------------------------------------------------------------------------------- /recyclerviewhelper/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'com.android.library' 18 | 19 | android { 20 | compileSdkVersion 26 21 | buildToolsVersion '27.0.3' 22 | 23 | defaultConfig { 24 | minSdkVersion 17 25 | targetSdkVersion 26 26 | versionCode 1 27 | versionName "1.0" 28 | } 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation fileTree(dir: 'libs', include: ['*.jar']) 39 | testImplementation 'junit:junit:4.12' 40 | compileOnly "com.android.support:appcompat-v7:25.1.0" 41 | compileOnly "com.android.support:recyclerview-v7:25.1.0" 42 | } 43 | 44 | if (project.hasProperty('publish') && project.getProperty('publish')) { 45 | // Bintray Params 46 | ext { 47 | bintrayRepo = 'maven' 48 | bintrayName = rootProject.ext.libBintrayName 49 | 50 | publishedGroupId = GROUP 51 | libraryName = rootProject.ext.libModuleName 52 | artifact = rootProject.ext.libModuleName // Has to be same as your library module name 53 | 54 | libraryDescription = rootProject.ext.libModuleDesc 55 | 56 | // Your github repo link 57 | siteUrl = rootProject.ext.libPomUrl 58 | gitUrl = rootProject.ext.libPomUrl + '.git' 59 | githubRepository = rootProject.ext.libGithubRepo 60 | 61 | libraryVersion = rootProject.ext.libVersionName 62 | 63 | developerId = POM_DEVELOPER_ID 64 | developerName = POM_DEVELOPER_NAME 65 | developerEmail = POM_DEVELOPER_EMAILID 66 | 67 | licenseName = POM_LICENCE_NAME 68 | licenseUrl = POM_LICENCE_URL 69 | allLicenses = [POM_ALL_LICENCES] 70 | } 71 | 72 | // Place it at the end of the file 73 | apply from: 'https://raw.githubusercontent.com/nisrulz/JCenter/master/installv1.gradle' 74 | apply from: 'https://raw.githubusercontent.com/nisrulz/JCenter/master/bintrayv1.gradle' 75 | 76 | 77 | javadoc { 78 | failOnError = false 79 | } 80 | } -------------------------------------------------------------------------------- /recyclerviewhelper/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/nishant/android_sdk/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 | -------------------------------------------------------------------------------- /recyclerviewhelper/src/androidTest/java/github/nisrulz/recyclerviewhelper/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package github.nisrulz.recyclerviewhelper; 18 | 19 | import android.app.Application; 20 | import android.test.ApplicationTestCase; 21 | 22 | /** 23 | * Testing Fundamentals 24 | */ 25 | public class ApplicationTest extends ApplicationTestCase { 26 | public ApplicationTest() { 27 | super(Application.class); 28 | } 29 | } -------------------------------------------------------------------------------- /recyclerviewhelper/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /recyclerviewhelper/src/main/java/github/nisrulz/recyclerviewhelper/RVHAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package github.nisrulz.recyclerviewhelper; 18 | 19 | /** 20 | * Interface to notify a RecyclerView.Adapter of moving and dismissal event 21 | */ 22 | public interface RVHAdapter { 23 | 24 | /** 25 | * Called when an item has been dragged far enough to trigger a move. This is called every time 26 | * an item is shifted, and not at the end of a "drop" event. 27 | * 28 | * Implementations should call RecyclerView.Adapter notifyItemMoved(int, int) after 29 | * adjusting the underlying data to reflect this move. 30 | * 31 | * @param fromPosition 32 | * The start position of the moved item. 33 | * @param toPosition 34 | * Then resolved position of the moved item. 35 | * @return True if the item was moved to the new adapter position. 36 | */ 37 | boolean onItemMove(int fromPosition, int toPosition); 38 | 39 | /** 40 | * Called when an item has been dismissed by a swipe. 41 | * 42 | * Implementations should call RecyclerView.Adapter notifyItemRemoved(int) after 43 | * adjusting the underlying data to reflect this removal. 44 | * 45 | * @param position 46 | * The position of the item dismissed. 47 | * @param direction 48 | * the direction 49 | */ 50 | void onItemDismiss(int position, int direction); 51 | void onItemSwipeDrag(float dX,float dY,int position); 52 | void onItemLongPress(float dX,float dY,int position); 53 | } 54 | -------------------------------------------------------------------------------- /recyclerviewhelper/src/main/java/github/nisrulz/recyclerviewhelper/RVHItemClickListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package github.nisrulz.recyclerviewhelper; 18 | 19 | import android.content.Context; 20 | import android.support.v7.widget.RecyclerView; 21 | import android.util.Log; 22 | import android.view.GestureDetector; 23 | import android.view.MotionEvent; 24 | import android.view.View; 25 | 26 | /** 27 | * The type Rvh item click listener. 28 | */ 29 | public class RVHItemClickListener implements RecyclerView.OnItemTouchListener { 30 | private final OnItemClickListener mListener; 31 | /** 32 | * The M gesture detector. 33 | */ 34 | private final GestureDetector mGestureDetector; 35 | 36 | /** 37 | * Instantiates a new Rvh item click listener. 38 | * 39 | * @param context 40 | * the context 41 | * @param listener 42 | * the listener 43 | */ 44 | public RVHItemClickListener(Context context, OnItemClickListener listener) { 45 | mListener = listener; 46 | mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { 47 | @Override 48 | public boolean onSingleTapUp(MotionEvent e) { 49 | return true; 50 | } 51 | 52 | @Override 53 | public boolean onDown(MotionEvent e) { 54 | return true; 55 | } 56 | 57 | @Override 58 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 59 | 60 | return true; 61 | } 62 | 63 | @Override 64 | public void onShowPress(MotionEvent e) { 65 | //Log.e("On Show Press","ShowPress"); 66 | mListener.onItemShowPress(longPressView,longPressPos,e.getX(),e.getY()); 67 | mListener.detectShowPress(true,longPressPos); 68 | //手指触摸屏幕,并且尚未松开或拖动。与onDown的区别是,onShowPress强调没用松开和没有拖动 69 | } 70 | 71 | @Override 72 | public void onLongPress(MotionEvent e){ 73 | mListener.onItemLongPress(longPressView,longPressPos,e.getX(),e.getY()); 74 | } 75 | 76 | 77 | }); 78 | } 79 | 80 | private View longPressView; 81 | private int longPressPos; 82 | 83 | @Override 84 | public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { 85 | View childView = view.findChildViewUnder(e.getX(), e.getY()); 86 | // if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e) && childView !=view.getChildAt(0)) { 87 | // 88 | // 89 | // if(e.getAction() == MotionEvent.ACTION_UP){ 90 | // Log.e("Here","Here"); 91 | // mListener.onItemTouchUp(childView, view.getChildAdapterPosition(childView),e.getX(),e.getY()); 92 | // } 93 | // else if (e.getAction() == MotionEvent.ACTION_DOWN){ 94 | // longPressView = childView; 95 | // longPressPos = view.getChildAdapterPosition(childView); 96 | // mListener.onItemTouchDown(childView, view.getChildAdapterPosition(childView),e.getX(),e.getY()); 97 | // } 98 | // return true; 99 | // 100 | // } 101 | 102 | if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e) ) { 103 | 104 | 105 | if(e.getAction() == MotionEvent.ACTION_UP){ 106 | Log.e("RVHClickListenr","UP"); 107 | mListener.detectShowPress(false,longPressPos); 108 | mListener.onItemTouchUp(childView, view.getChildAdapterPosition(childView),e.getX(),e.getY()); 109 | return false; 110 | } 111 | else if (e.getAction() == MotionEvent.ACTION_DOWN){ 112 | Log.e("RVHClickListenr","Down"); 113 | longPressView = childView; 114 | longPressPos = view.getChildAdapterPosition(childView); 115 | mListener.onItemTouchDown(childView, view.getChildAdapterPosition(childView),e.getX(),e.getY()); 116 | //让事件继续传递,从 rv 到 adapter 的 item里面 117 | return false; 118 | } 119 | 120 | else if (e.getAction() == MotionEvent.ACTION_MOVE){ 121 | //Log.e("RVHClickListenr","MOVE"); 122 | //让事件继续传递,从 rv 到 adapter 的 item里面 123 | mListener.onItemTouchMove(childView, view.getChildAdapterPosition(childView),e.getX(),e.getY()); 124 | return false; 125 | } 126 | else if (e.getAction() == MotionEvent.ACTION_CANCEL){ 127 | //Log.e("RVHClickListenr","CANCEL"); 128 | //让事件继续传递,从 rv 到 adapter 的 item里面 129 | return false; 130 | } 131 | 132 | } 133 | 134 | return false; 135 | } 136 | 137 | @Override 138 | public void onTouchEvent(RecyclerView view, MotionEvent e) { 139 | // Do nothing 140 | } 141 | 142 | @Override 143 | public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 144 | // Do nothings 145 | } 146 | 147 | /** 148 | * The interface On item click listener. 149 | */ 150 | public interface OnItemClickListener { 151 | /** 152 | * On item click. 153 | * 154 | * @param view 155 | * the view 156 | * @param position 157 | * the position 158 | */ 159 | void onItemTouchUp(View view, int position,float dX,float dY); 160 | void onItemTouchDown(View view, int position,float dX,float dY); 161 | void onItemTouchMove(View view, int position,float dX,float dY); 162 | void onItemLongPress(View view,int position,float dX,float dY); 163 | void onItemShowPress(View view,int position,float dX,float dY); 164 | void onOutsideTouch(float dX,float dY); 165 | void detectShowPress(boolean boo,int position); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /recyclerviewhelper/src/main/java/github/nisrulz/recyclerviewhelper/RVHItemDividerDecoration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package github.nisrulz.recyclerviewhelper; 18 | 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.graphics.Canvas; 22 | import android.graphics.Rect; 23 | import android.graphics.drawable.Drawable; 24 | import android.support.v7.widget.LinearLayoutManager; 25 | import android.support.v7.widget.RecyclerView; 26 | import android.view.View; 27 | 28 | /** 29 | * The type Rvh item divider decoration. 30 | */ 31 | public class RVHItemDividerDecoration extends RecyclerView.ItemDecoration { 32 | 33 | /** 34 | * The constant HORIZONTAL_LIST. 35 | */ 36 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; 37 | /** 38 | * The constant VERTICAL_LIST. 39 | */ 40 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; 41 | private static final int[] ATTRS = new int[] { 42 | android.R.attr.listDivider 43 | }; 44 | private final Drawable mDivider; 45 | 46 | private int mOrientation; 47 | 48 | /** 49 | * Instantiates a new Rvh item divider decoration. 50 | * 51 | * @param context 52 | * the context 53 | * @param orientation 54 | * the orientation 55 | */ 56 | public RVHItemDividerDecoration(Context context, int orientation) { 57 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 58 | mDivider = a.getDrawable(0); 59 | a.recycle(); 60 | setOrientation(orientation); 61 | } 62 | 63 | /** 64 | * Sets orientation. 65 | * 66 | * @param orientation 67 | * the orientation 68 | */ 69 | public void setOrientation(int orientation) { 70 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { 71 | throw new IllegalArgumentException("invalid orientation"); 72 | } 73 | mOrientation = orientation; 74 | } 75 | 76 | @Override 77 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 78 | super.onDraw(c, parent, state); 79 | 80 | if (mOrientation == VERTICAL_LIST) { 81 | drawVertical(c, parent); 82 | } else { 83 | drawHorizontal(c, parent); 84 | } 85 | } 86 | 87 | /** 88 | * Draw vertical. 89 | * 90 | * @param c 91 | * the c 92 | * @param parent 93 | * the parent 94 | */ 95 | private void drawVertical(Canvas c, RecyclerView parent) { 96 | final int left = parent.getPaddingLeft(); 97 | final int right = parent.getWidth() - parent.getPaddingRight(); 98 | 99 | final int childCount = parent.getChildCount(); 100 | for (int i = 0; i < childCount; i++) { 101 | final View child = parent.getChildAt(i); 102 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); 103 | final int top = child.getBottom() + params.bottomMargin + Math.round(child.getTranslationY()); 104 | final int bottom = top + mDivider.getIntrinsicHeight(); 105 | mDivider.setBounds(left, top, right, bottom); 106 | mDivider.draw(c); 107 | } 108 | } 109 | 110 | /** 111 | * Draw horizontal. 112 | * 113 | * @param c 114 | * the c 115 | * @param parent 116 | * the parent 117 | */ 118 | private void drawHorizontal(Canvas c, RecyclerView parent) { 119 | final int top = parent.getPaddingTop(); 120 | final int bottom = parent.getHeight() - parent.getPaddingBottom(); 121 | 122 | final int childCount = parent.getChildCount(); 123 | for (int i = 0; i < childCount; i++) { 124 | final View child = parent.getChildAt(i); 125 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); 126 | final int left = child.getRight() + params.rightMargin + Math.round(child.getTranslationX()); 127 | final int right = left + mDivider.getIntrinsicHeight(); 128 | mDivider.setBounds(left, top, right, bottom); 129 | mDivider.draw(c); 130 | } 131 | } 132 | 133 | @Override 134 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 135 | RecyclerView.State state) { 136 | super.getItemOffsets(outRect, view, parent, state); 137 | 138 | if (mOrientation == VERTICAL_LIST) { 139 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 140 | } else { 141 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /recyclerviewhelper/src/main/java/github/nisrulz/recyclerviewhelper/RVHItemTouchHelperCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package github.nisrulz.recyclerviewhelper; 18 | 19 | import android.graphics.Canvas; 20 | import android.support.v7.widget.RecyclerView; 21 | import android.util.Log; 22 | 23 | import static android.support.v7.widget.helper.ItemTouchHelper.ACTION_STATE_IDLE; 24 | import static android.support.v7.widget.helper.ItemTouchHelper.ACTION_STATE_SWIPE; 25 | import static android.support.v7.widget.helper.ItemTouchHelper.Callback; 26 | import static android.support.v7.widget.helper.ItemTouchHelper.DOWN; 27 | import static android.support.v7.widget.helper.ItemTouchHelper.END; 28 | import static android.support.v7.widget.helper.ItemTouchHelper.START; 29 | import static android.support.v7.widget.helper.ItemTouchHelper.UP; 30 | 31 | /** 32 | * The type Rvh item touch helper callback. 33 | */ 34 | public class RVHItemTouchHelperCallback extends Callback { 35 | 36 | @Deprecated 37 | private final RVHAdapter mAdapter; 38 | 39 | private final boolean isLongPressDragEnabled; 40 | private final boolean isItemViewSwipeEnabledLeft; 41 | private final boolean isItemViewSwipeEnabledRight; 42 | public boolean disableSwipe; 43 | public int orginIndex; 44 | 45 | public RecyclerView referenceRV; 46 | 47 | /** 48 | * Instantiates a new Rvh item touch helper callback. 49 | * 50 | * @param adapter 51 | * the adapter 52 | * @param isLongPressDragEnabled 53 | * the is long press drag enabled 54 | * @param isItemViewSwipeEnabledLeft 55 | * the is item view swipe enabled left 56 | * @param isItemViewSwipeEnabledRight 57 | * the is item view swipe enabled right 58 | */ 59 | public RVHItemTouchHelperCallback(RVHAdapter adapter, boolean isLongPressDragEnabled, 60 | boolean isItemViewSwipeEnabledLeft, boolean isItemViewSwipeEnabledRight) { 61 | mAdapter = adapter; 62 | this.isItemViewSwipeEnabledLeft = isItemViewSwipeEnabledLeft; 63 | this.isItemViewSwipeEnabledRight = isItemViewSwipeEnabledRight; 64 | this.isLongPressDragEnabled = isLongPressDragEnabled; 65 | } 66 | 67 | @Override 68 | public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { 69 | final int dragFlags = UP | DOWN | START | END; // UP | DOWN 70 | final int swipeFlags; 71 | if (isItemViewSwipeEnabledLeft && isItemViewSwipeEnabledRight) { 72 | swipeFlags = START | END ; // START | END 73 | } 74 | else if (isItemViewSwipeEnabledRight) { 75 | swipeFlags = START; 76 | } 77 | else { 78 | swipeFlags = END; 79 | } 80 | 81 | return Callback.makeMovementFlags(dragFlags, swipeFlags); 82 | } 83 | 84 | @Override 85 | public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, 86 | RecyclerView.ViewHolder target) { 87 | return current.getItemViewType() == target.getItemViewType(); 88 | } 89 | 90 | @Override 91 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, 92 | RecyclerView.ViewHolder target) { 93 | // Notify the adapter of the move 94 | ((RVHAdapter) referenceRV.getAdapter()).onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); 95 | return true; 96 | } 97 | 98 | @Override 99 | public boolean isLongPressDragEnabled() { 100 | return isLongPressDragEnabled; 101 | } 102 | 103 | @Override 104 | public boolean isItemViewSwipeEnabled() { 105 | 106 | if(disableSwipe){ 107 | return false; 108 | } 109 | else{ 110 | 111 | return isItemViewSwipeEnabledLeft || isItemViewSwipeEnabledRight; 112 | } 113 | } 114 | 115 | @Override 116 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { 117 | Log.e("Swipe","On Swipe!"); 118 | ((RVHAdapter) referenceRV.getAdapter()).onItemDismiss(viewHolder.getAdapterPosition(), direction); 119 | } 120 | 121 | @Override 122 | public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { 123 | // We only want the active item to change 124 | if (actionState != ACTION_STATE_IDLE && viewHolder instanceof RVHViewHolder) { 125 | // Let the view holder know that this item is being moved or dragged 126 | RVHViewHolder itemViewHolder = (RVHViewHolder) viewHolder; 127 | itemViewHolder.onItemSelected(actionState,viewHolder.getAdapterPosition()); 128 | } 129 | 130 | super.onSelectedChanged(viewHolder, actionState); 131 | } 132 | 133 | @Override 134 | public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { 135 | super.clearView(recyclerView, viewHolder); 136 | 137 | if (viewHolder instanceof RVHViewHolder) { 138 | // Tell the view holder it's time to restore the idle state 139 | RVHViewHolder itemViewHolder = (RVHViewHolder) viewHolder; 140 | itemViewHolder.onItemClear(); 141 | } 142 | } 143 | 144 | @Override 145 | public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, 146 | float dX, float dY, int actionState, boolean isCurrentlyActive) { 147 | if (actionState == ACTION_STATE_SWIPE ) { 148 | // Fade out the view as it is swiped out of the parent's bounds 149 | 150 | if(orginIndex !=0){ 151 | 152 | viewHolder.itemView.setTranslationX(dX); 153 | viewHolder.itemView.setTranslationY(dY); // 154 | ((RVHAdapter) referenceRV.getAdapter()).onItemSwipeDrag(dX,dY,viewHolder.getAdapterPosition()); // 155 | } 156 | 157 | 158 | // viewHolder.itemView.setTranslationX(dX); 159 | // viewHolder.itemView.setTranslationY(dY); // 160 | // ((RVHAdapter) referenceRV.getAdapter()).onItemSwipeDrag(dX,dY,viewHolder.getAdapterPosition()); // 161 | } 162 | else { 163 | //super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); 164 | 165 | if(orginIndex !=0){ 166 | ((RVHAdapter) referenceRV.getAdapter()).onItemLongPress(dX,dY,viewHolder.getAdapterPosition()); 167 | } 168 | //Log.e("Here","here"); 169 | } 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /recyclerviewhelper/src/main/java/github/nisrulz/recyclerviewhelper/RVHViewHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package github.nisrulz.recyclerviewhelper; 18 | 19 | /** 20 | * The interface Rvh view holder. 21 | */ 22 | public interface RVHViewHolder { 23 | 24 | /** 25 | * Called when the ItemTouchHelper first registers an item as being moved or swiped. 26 | * Implementations should update the item view to indicate it's active state. 27 | * 28 | * @param actionstate 29 | * the actionstate 30 | */ 31 | void onItemSelected(int actionstate,int position); 32 | 33 | /** 34 | * Called when the ItemTouchHelper has completed the move or swipe, and the active item 35 | * state should be cleared. 36 | */ 37 | void onItemClear(); 38 | } 39 | -------------------------------------------------------------------------------- /recyclerviewhelper/src/test/java/github/nisrulz/recyclerviewhelper/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Nishant Srivastava 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package github.nisrulz.recyclerviewhelper; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | 23 | /** 24 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 25 | */ 26 | public class ExampleUnitTest { 27 | @Test 28 | public void addition_isCorrect() throws Exception { 29 | assertEquals(4, 2 + 2); 30 | } 31 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':springylib',':liboverscroll',':supportrenderscriptblur',':recyclerviewhelper' 2 | -------------------------------------------------------------------------------- /springylib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /springylib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '27.0.3' 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(dir: 'libs', include: ['*.jar']) 26 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 27 | exclude group: 'com.android.support', module: 'support-annotations' 28 | }) 29 | implementation 'com.android.support:appcompat-v7:26.+' 30 | implementation 'com.android.support:recyclerview-v7:25.1.0' 31 | testImplementation 'junit:junit:4.12' 32 | implementation 'com.facebook.rebound:rebound:0.3.8' 33 | } 34 | -------------------------------------------------------------------------------- /springylib/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 D:\Softwares\Study_Softwares\android-studio-document\sdk/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /springylib/src/androidTest/java/com/zach/salman/springylib/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.zach.salman.springylib; 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.zach.salman.springylib.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /springylib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /springylib/src/main/java/com/zach/salman/springylib/SpringAnimationType.java: -------------------------------------------------------------------------------- 1 | package com.zach.salman.springylib; 2 | 3 | /** 4 | * Created by salman on 21/11/16. 5 | */ 6 | 7 | public enum SpringAnimationType { 8 | TRANSLATEX, 9 | TRANSLATEY, 10 | ROTATEX, 11 | ROTATEY, 12 | SCALEXY, 13 | SCALEX, 14 | SCALEY, 15 | ALPHA, 16 | ROTATION 17 | } 18 | -------------------------------------------------------------------------------- /springylib/src/main/java/com/zach/salman/springylib/SpringyAnimator.java: -------------------------------------------------------------------------------- 1 | package com.zach.salman.springylib; 2 | 3 | import android.view.View; 4 | 5 | import com.facebook.rebound.SimpleSpringListener; 6 | import com.facebook.rebound.Spring; 7 | import com.facebook.rebound.SpringConfig; 8 | import com.facebook.rebound.SpringSystem; 9 | import com.facebook.rebound.SpringUtil; 10 | 11 | 12 | /** 13 | * Created by salman on 17/11/16. 14 | */ 15 | 16 | public class SpringyAnimator { 17 | private float startValue, endValue; 18 | private static final float DEFAULT_TENSION = 40; 19 | private static final float DEFAULT_FRACTION = 7; 20 | private double tension,fraction; 21 | private SpringSystem springSystem; 22 | private SpringAnimationType animationType; 23 | private SpringyListener springAnimatorListener; 24 | private int delay = 0; 25 | 26 | 27 | /** 28 | * Constructor for with Animation Type + Spring config + animation Values 29 | * * @param springConfig config class for the spring 30 | * @param type SpringyAnimationType instance for animation type 31 | * @param tension Spring tension for animation type 32 | * @param fraction Spring fraction value for animation 33 | * @param startValue where animation start from 34 | * @param endValue where animation ends to 35 | * **/ 36 | 37 | 38 | public SpringyAnimator(SpringAnimationType type, double tension, 39 | double fraction, float startValue, float endValue) { 40 | this.tension = tension; 41 | this.fraction = fraction; 42 | this.startValue = startValue; 43 | this.endValue = endValue; 44 | springSystem = SpringSystem.create(); 45 | animationType = type; 46 | 47 | } 48 | 49 | 50 | 51 | /** 52 | * Constructor for with Animation Type + default config for spring + animation Values 53 | * * @param springConfig config class for the spring 54 | * @param type SpringyAnimationType instance for animation type 55 | * @param startValue where animation start from 56 | * @param endValue where animation ends to 57 | * **/ 58 | public SpringyAnimator(SpringAnimationType type, float startValue, 59 | float endValue) { 60 | this.tension = DEFAULT_TENSION; 61 | this.fraction = DEFAULT_FRACTION; 62 | this.startValue = startValue; 63 | this.endValue = endValue; 64 | springSystem = SpringSystem.create(); 65 | animationType = type; 66 | 67 | } 68 | 69 | 70 | 71 | 72 | /** 73 | * @param delay int value for SpringyAnimation delay each item if we have multiple items in 74 | * animation raw. 75 | * **/ 76 | public void setDelay(int delay) { 77 | this.delay = delay; 78 | } 79 | 80 | public void startSpring(final View view) { 81 | setInitValue(view); 82 | Runnable startAnimation = new Runnable() { 83 | @Override 84 | public void run() { 85 | Spring spring = springSystem.createSpring(); 86 | spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(tension, fraction)); 87 | spring.addListener(new SimpleSpringListener() { 88 | @Override 89 | public void onSpringUpdate(Spring spring) { 90 | view.setVisibility(View.VISIBLE); 91 | final float value = (float) SpringUtil.mapValueFromRangeToRange(spring.getCurrentValue(), 0, 1, startValue, endValue); 92 | switch (animationType) { 93 | case TRANSLATEY: 94 | view.setTranslationY(value); 95 | break; 96 | case TRANSLATEX: 97 | view.setTranslationX(value); 98 | break; 99 | case ALPHA: 100 | view.setAlpha(value); 101 | break; 102 | case SCALEY: 103 | view.setScaleY(value); 104 | break; 105 | case SCALEX: 106 | view.setScaleX(value); 107 | break; 108 | case SCALEXY: 109 | view.setScaleY(value); 110 | view.setScaleX(value); 111 | break; 112 | case ROTATEY: 113 | view.setRotationY(value); 114 | break; 115 | case ROTATEX: 116 | view.setRotationX(value); 117 | break; 118 | case ROTATION: 119 | view.setRotation(value); 120 | break; 121 | } 122 | } 123 | 124 | @Override 125 | public void onSpringAtRest(Spring spring) { 126 | if (springAnimatorListener != null){ 127 | springAnimatorListener.onSpringStop(); 128 | } 129 | } 130 | 131 | @Override 132 | public void onSpringActivate(Spring spring) { 133 | if (springAnimatorListener != null){ 134 | springAnimatorListener.onSpringStart(); 135 | } 136 | } 137 | 138 | }); 139 | spring.setEndValue(1); 140 | } 141 | }; 142 | view.postDelayed(startAnimation, delay); 143 | 144 | } 145 | 146 | /** 147 | * @param view instance for set pre animation value 148 | * **/ 149 | private void setInitValue(View view) { 150 | switch (animationType) { 151 | case TRANSLATEY: 152 | view.setTranslationY(startValue); 153 | break; 154 | case TRANSLATEX: 155 | view.setTranslationX(startValue); 156 | break; 157 | case ALPHA: 158 | view.setAlpha(startValue); 159 | break; 160 | case SCALEY: 161 | view.setScaleY(startValue); 162 | break; 163 | case SCALEX: 164 | view.setScaleX(startValue); 165 | break; 166 | case SCALEXY: 167 | view.setScaleY(startValue); 168 | view.setScaleX(startValue); 169 | break; 170 | case ROTATEY: 171 | view.setRotationY(startValue); 172 | break; 173 | case ROTATEX: 174 | view.setRotationX(startValue); 175 | break; 176 | case ROTATION: 177 | view.setRotation(startValue); 178 | break; 179 | } 180 | } 181 | 182 | /* 183 | * Springy Listener to track the Spring 184 | * */ 185 | public void setSpringyListener(SpringyListener springyListener) { 186 | this.springAnimatorListener = springyListener; 187 | } 188 | 189 | 190 | } 191 | -------------------------------------------------------------------------------- /springylib/src/main/java/com/zach/salman/springylib/SpringyListener.java: -------------------------------------------------------------------------------- 1 | package com.zach.salman.springylib; 2 | 3 | /** 4 | * Created by Salman_Zach on 11/25/2016. 5 | */ 6 | 7 | public interface SpringyListener { 8 | 9 | /* 10 | * hits when Spring is Active 11 | * */ 12 | void onSpringStart(); 13 | 14 | /* 15 | * hits when Spring is inActive 16 | * */ 17 | 18 | void onSpringStop(); 19 | } 20 | -------------------------------------------------------------------------------- /springylib/src/main/java/com/zach/salman/springylib/springyRecyclerView/SpringyAdapterAnimationType.java: -------------------------------------------------------------------------------- 1 | package com.zach.salman.springylib.springyRecyclerView; 2 | 3 | /** 4 | * Created by Zach on 7/1/2017. 5 | */ 6 | 7 | public enum SpringyAdapterAnimationType { 8 | SLIDE_FROM_TOP, 9 | SLIDE_FROM_BOTTOM, 10 | SLIDE_FROM_RIGHT, 11 | SLIDE_FROM_LEFT, 12 | SPREAD, 13 | SCALE, 14 | NULL, 15 | DIVE 16 | 17 | } 18 | -------------------------------------------------------------------------------- /springylib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SpringyLib 3 | 4 | -------------------------------------------------------------------------------- /springylib/src/test/java/com/zach/salman/springylib/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.zach.salman.springylib; 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 | } -------------------------------------------------------------------------------- /supportrenderscriptblur/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /supportrenderscriptblur/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | PUBLISH_GROUP_ID = 'com.eightbitlab' 5 | PUBLISH_ARTIFACT_ID = 'supportrenderscriptblur' 6 | PUBLISH_VERSION = '1.0.0' 7 | } 8 | 9 | android { 10 | compileSdkVersion 25 11 | buildToolsVersion '27.0.3' 12 | 13 | defaultConfig { 14 | renderscriptTargetApi 25 15 | renderscriptSupportModeEnabled true 16 | minSdkVersion 14 17 | targetSdkVersion 25 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(dir: 'libs', include: ['*.jar']) 34 | implementation 'com.android.support:appcompat-v7:25.2.0' 35 | compileOnly 'com.eightbitlab:blurview:1.3.2' 36 | } 37 | 38 | apply plugin: 'maven' 39 | 40 | def groupId = project.PUBLISH_GROUP_ID 41 | def artifactId = project.PUBLISH_ARTIFACT_ID 42 | def version = project.PUBLISH_VERSION 43 | 44 | def localReleaseDest = "${buildDir}/release/${version}" 45 | 46 | task androidJavadocs(type: Javadoc) { 47 | failOnError = false 48 | source = android.sourceSets.main.java.srcDirs 49 | ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" 50 | classpath += files(ext.androidJar) 51 | } 52 | 53 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 54 | classifier = 'javadoc' 55 | from androidJavadocs.destinationDir 56 | } 57 | 58 | task androidSourcesJar(type: Jar) { 59 | classifier = 'sources' 60 | from android.sourceSets.main.java.srcDirs 61 | } 62 | 63 | uploadArchives { 64 | repositories.mavenDeployer { 65 | pom.groupId = groupId 66 | pom.artifactId = artifactId 67 | pom.version = version 68 | // Add other pom properties here if you want (developer details / licenses) 69 | repository(url: "file://${localReleaseDest}") 70 | } 71 | } 72 | 73 | task zipRelease(type: Zip) { 74 | from localReleaseDest 75 | destinationDir buildDir 76 | archiveName "release-${version}.zip" 77 | } 78 | 79 | task generateRelease << { 80 | println "Release ${version} can be found at ${localReleaseDest}/" 81 | println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip" 82 | } 83 | 84 | generateRelease.dependsOn(uploadArchives) 85 | generateRelease.dependsOn(zipRelease) 86 | 87 | artifacts { 88 | archives androidSourcesJar 89 | archives androidJavadocsJar 90 | } -------------------------------------------------------------------------------- /supportrenderscriptblur/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /supportrenderscriptblur/src/main/java/com/eightbitlab/supportrenderscriptblur/SupportRenderScriptBlur.java: -------------------------------------------------------------------------------- 1 | package com.eightbitlab.supportrenderscriptblur; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.support.annotation.NonNull; 6 | import android.support.v8.renderscript.Allocation; 7 | import android.support.v8.renderscript.Element; 8 | import android.support.v8.renderscript.RenderScript; 9 | import android.support.v8.renderscript.ScriptIntrinsicBlur; 10 | 11 | import eightbitlab.com.blurview.BlurAlgorithm; 12 | 13 | /** 14 | * Blur using RenderScript, processed on GPU. 15 | * Uses Renderscript from support library 16 | */ 17 | public final class SupportRenderScriptBlur implements BlurAlgorithm { 18 | private final RenderScript renderScript; 19 | private final ScriptIntrinsicBlur blurScript; 20 | private Allocation outAllocation; 21 | 22 | private int lastBitmapWidth = -1; 23 | private int lastBitmapHeight = -1; 24 | 25 | /** 26 | * @param context Context to create the {@link RenderScript} 27 | */ 28 | public SupportRenderScriptBlur(Context context) { 29 | renderScript = RenderScript.create(context); 30 | blurScript = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript)); 31 | } 32 | 33 | private boolean canReuseAllocation(Bitmap bitmap) { 34 | return bitmap.getHeight() == lastBitmapHeight && bitmap.getWidth() == lastBitmapWidth; 35 | } 36 | 37 | /** 38 | * @param bitmap bitmap to blur 39 | * @param blurRadius blur radius (1..25) 40 | * @return blurred bitmap 41 | */ 42 | @Override 43 | public final Bitmap blur(Bitmap bitmap, float blurRadius) { 44 | //Allocation will use the same backing array of pixels as bitmap if created with USAGE_SHARED flag 45 | Allocation inAllocation = Allocation.createFromBitmap(renderScript, bitmap); 46 | 47 | if (!canReuseAllocation(bitmap)) { 48 | if (outAllocation != null) { 49 | outAllocation.destroy(); 50 | } 51 | outAllocation = Allocation.createTyped(renderScript, inAllocation.getType()); 52 | lastBitmapWidth = bitmap.getWidth(); 53 | lastBitmapHeight = bitmap.getHeight(); 54 | } 55 | 56 | blurScript.setRadius(blurRadius); 57 | blurScript.setInput(inAllocation); 58 | //do not use inAllocation in forEach. it will cause visual artifacts on blurred Bitmap 59 | blurScript.forEach(outAllocation); 60 | outAllocation.copyTo(bitmap); 61 | 62 | inAllocation.destroy(); 63 | return bitmap; 64 | } 65 | 66 | @Override 67 | public final void destroy() { 68 | blurScript.destroy(); 69 | renderScript.destroy(); 70 | if (outAllocation != null) { 71 | outAllocation.destroy(); 72 | } 73 | } 74 | 75 | @Override 76 | public boolean canModifyBitmap() { 77 | return true; 78 | } 79 | 80 | @NonNull 81 | @Override 82 | public Bitmap.Config getSupportedBitmapConfig() { 83 | return Bitmap.Config.ARGB_8888; 84 | } 85 | } 86 | --------------------------------------------------------------------------------