├── .idea ├── .name ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── vcs.xml ├── modules.xml ├── runConfigurations.xml ├── compiler.xml ├── gradle.xml └── misc.xml ├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable │ │ │ │ ├── ic_image_loadfail.png │ │ │ │ └── ic_image_loading.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── values-v21 │ │ │ │ └── styles.xml │ │ │ ├── menu │ │ │ │ └── menu_main.xml │ │ │ └── layout │ │ │ │ ├── content_main.xml │ │ │ │ ├── fragment_newslist.xml │ │ │ │ ├── footer.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── item_news.xml │ │ │ │ └── activity_news_detail.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── hansheng │ │ │ │ └── simplenba │ │ │ │ ├── nba │ │ │ │ ├── presenter │ │ │ │ │ ├── NbaDetailPresenter.java │ │ │ │ │ ├── NbaPresenter.java │ │ │ │ │ ├── NbaDetailPresenterImpl.java │ │ │ │ │ └── NewsPresenterImpl.java │ │ │ │ ├── view │ │ │ │ │ ├── NbaDetailView.java │ │ │ │ │ └── NbaView.java │ │ │ │ ├── model │ │ │ │ │ ├── NbaModel.java │ │ │ │ │ └── NbaModelImpl.java │ │ │ │ ├── NewsJsonUtils.java │ │ │ │ ├── widget │ │ │ │ │ ├── NbasFragment.java │ │ │ │ │ ├── NbaDetailActivity.java │ │ │ │ │ ├── DividerItemDecoration.java │ │ │ │ │ └── NewListFragment.java │ │ │ │ └── NbaAdapter.java │ │ │ │ ├── beans │ │ │ │ ├── NextId.java │ │ │ │ ├── NbaDetailBean.java │ │ │ │ └── NbaBean.java │ │ │ │ ├── commons │ │ │ │ └── Urls.java │ │ │ │ ├── utils │ │ │ │ ├── ImageLoaderUtils.java │ │ │ │ ├── LogUtils.java │ │ │ │ ├── JsonUtils.java │ │ │ │ ├── ToolsUtil.java │ │ │ │ └── OkHttpUtils.java │ │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── hansheng │ │ │ └── simplenba │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── hansheng │ │ └── simplenba │ │ └── ApplicationTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── screenshot └── nba.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── swipeback ├── libs │ └── android-support-v4.jar ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── drawable-xhdpi │ │ │ ├── shadow_left.png │ │ │ ├── shadow_bottom.png │ │ │ └── shadow_right.png │ │ ├── layout │ │ │ └── swipeback_layout.xml │ │ └── values │ │ │ ├── styles.xml │ │ │ └── attrs.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── me │ │ └── imid │ │ └── swipebacklayout │ │ └── lib │ │ ├── app │ │ ├── SwipeBackActivityBase.java │ │ ├── SwipeBackPreferenceActivity.java │ │ ├── SwipeBackActivity.java │ │ └── SwipeBackActivityHelper.java │ │ ├── Utils.java │ │ ├── SwipeBackLayout.java │ │ └── ViewDragHelper.java └── build.gradle ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | SimpleNBA -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':swipeback' 2 | -------------------------------------------------------------------------------- /screenshot/nba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/screenshot/nba.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /swipeback/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/swipeback/libs/android-support-v4.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_image_loadfail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/app/src/main/res/drawable/ic_image_loadfail.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_image_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/app/src/main/res/drawable/ic_image_loading.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /swipeback/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /swipeback/src/main/res/drawable-xhdpi/shadow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/swipeback/src/main/res/drawable-xhdpi/shadow_left.png -------------------------------------------------------------------------------- /swipeback/src/main/res/drawable-xhdpi/shadow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/swipeback/src/main/res/drawable-xhdpi/shadow_bottom.png -------------------------------------------------------------------------------- /swipeback/src/main/res/drawable-xhdpi/shadow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrWu94/SimpleNBA/HEAD/swipeback/src/main/res/drawable-xhdpi/shadow_right.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/presenter/NbaDetailPresenter.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.presenter; 2 | 3 | /** 4 | * Created by hansheng on 2016/3/28. 5 | */ 6 | public interface NbaDetailPresenter { 7 | void loadNewsDetail(String docId); 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/presenter/NbaPresenter.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.presenter; 2 | 3 | /** 4 | * Created by hansheng on 2016/3/26. 5 | */ 6 | public interface NbaPresenter { 7 | void loadNews(); 8 | void loadNews(String type,String bas); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /swipeback/src/main/res/layout/swipeback_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/view/NbaDetailView.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.view; 2 | 3 | /** 4 | * Created by hansheng on 2016/3/26. 5 | */ 6 | public interface NbaDetailView { 7 | void showNewsDetialContent(String newsDetailContent); 8 | 9 | void showProgress(); 10 | 11 | void hideProgress(); 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/beans/NextId.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.beans; 2 | 3 | /** 4 | * Created by hansheng on 2016/3/28. 5 | */ 6 | public class NextId { 7 | private String nextId; 8 | public String getNextId() { 9 | return nextId; 10 | } 11 | 12 | public void setNextId(String nextId) { 13 | this.nextId = nextId; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/model/NbaModel.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.model; 2 | 3 | /** 4 | * Created by hansheng on 2016/3/26. 5 | */ 6 | public interface NbaModel { 7 | void loadNews(String url,String type, String bas,NbaModelImpl.OnLoadNewsListListener listener); 8 | 9 | void loadNewsDetail(String docid, NbaModelImpl.OnLoadNewsDetailListener listener); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SimpleNBA 3 | Settings 4 | 5 | 新闻 6 | 博客 7 | 汽车 8 | 笑话 9 | transition_news_img 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /swipeback/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/test/java/com/hansheng/simplenba/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /swipeback/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/view/NbaView.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.view; 2 | 3 | import com.hansheng.simplenba.beans.NbaBean; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by hansheng on 2016/3/26. 9 | */ 10 | public interface NbaView { 11 | void showProgress(); 12 | 13 | void addNews(List newsList); 14 | 15 | void hideProgress(); 16 | 17 | void showLoadFailMsg(); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hansheng/simplenba/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | 8 | 9 | 5dp 10 | 86dp 11 | 60dp 12 | 13 | 16sp 14 | 14sp 15 | 12sp 16 | 17 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /swipeback/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackActivityBase.java: -------------------------------------------------------------------------------- 1 | package me.imid.swipebacklayout.lib.app; 2 | 3 | import me.imid.swipebacklayout.lib.SwipeBackLayout; 4 | /** 5 | * @author Yrom 6 | */ 7 | public interface SwipeBackActivityBase { 8 | /** 9 | * @return the SwipeBackLayout associated with this activity. 10 | */ 11 | public abstract SwipeBackLayout getSwipeBackLayout(); 12 | 13 | public abstract void setSwipeBackEnable(boolean enable); 14 | 15 | /** 16 | * Scroll out contentView and finish the activity 17 | */ 18 | public abstract void scrollToFinishActivity(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #212121 7 | #727272 8 | 9 | #F44336 10 | #D32F2F 11 | #FFCDD2 12 | #CDDC39 13 | #FFFFFF 14 | #B6B6B6 15 | 16 | -------------------------------------------------------------------------------- /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 E:\androidsdk/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 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/beans/NbaDetailBean.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.beans; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by hansheng on 2016/3/26. 7 | */ 8 | public class NbaDetailBean implements Serializable { 9 | 10 | private String author; 11 | 12 | 13 | private String content; 14 | 15 | public String getAuthor() { 16 | return author; 17 | } 18 | 19 | public void setAuthor(String author) { 20 | this.author = author; 21 | } 22 | 23 | 24 | 25 | public String getContent() { 26 | return content; 27 | } 28 | 29 | public void setContent(String content) { 30 | this.content = content; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /swipeback/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_newslist.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/commons/Urls.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.commons; 2 | 3 | /** 4 | * Created by hansheng on 2016/3/26. 5 | */ 6 | public class Urls { 7 | 8 | //http://c.m.163.com/nc/article/headline/T1348647909107/0-5.html 头条 9 | // http://reader.res.meizu.com/reader/articlecontent/20151204/116409942.json 10 | //http://nbaplus.sinaapp.com/api/v1.0/news/update 11 | 12 | public static final int PAZE_SIZE = 20; 13 | 14 | public static final String HOST = "http://c.m.163.com/"; 15 | public static final String END_URL = "-" + PAZE_SIZE + ".html"; 16 | public static final String END_DETAIL_URL = "/full.html"; 17 | public static final String NBA ="http://nbaplus.sinaapp.com/api/v1.0/news/update"; 18 | public static final String BLOG ="http://nbaplus.sinaapp.com/api/v1.0/blogs/update"; 19 | 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | ANDROID_BUILD_TARGET_SDK_VERSION=23 21 | VERSION_NAME=1.0.0 22 | ANDROID_BUILD_COMPILE_SDK_VERSION=23 23 | ANDROID_BUILD_TOOLS_VERSION=23.0.2 24 | VERSION_CODE=1 25 | MIN_SDK_VERSION=14 -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.hansheng.simplenba" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.2.1' 26 | compile 'com.android.support:design:23.2.1' 27 | compile 'com.android.support:cardview-v7:23.1.1' 28 | compile 'com.squareup.okhttp:okhttp:2.7.0' 29 | compile 'com.google.code.gson:gson:2.2.4' 30 | compile 'de.hdodenhof:circleimageview:1.3.0' 31 | compile 'com.github.bumptech.glide:glide:3.6.1' 32 | compile 'org.sufficientlysecure:html-textview:1.3' 33 | compile project(':swipeback') 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/utils/ImageLoaderUtils.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.utils; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.bumptech.glide.Glide; 7 | import com.hansheng.simplenba.R; 8 | 9 | 10 | /** 11 | * Description : 图片加载工具类 12 | * Author : lauren 13 | * Email : lauren.liuling@gmail.com 14 | * Blog : http://www.liuling123.com 15 | * Date : 15/12/21 16 | */ 17 | public class ImageLoaderUtils { 18 | 19 | public static void display(Context context, ImageView imageView, String url, int placeholder, int error) { 20 | if(imageView == null) { 21 | throw new IllegalArgumentException("argument error"); 22 | } 23 | Glide.with(context).load(url).placeholder(placeholder) 24 | .error(error).crossFade().into(imageView); 25 | } 26 | 27 | public static void display(Context context, ImageView imageView, String url) { 28 | if(imageView == null) { 29 | throw new IllegalArgumentException("argument error"); 30 | } 31 | Glide.with(context).load(url).placeholder(R.drawable.ic_image_loading) 32 | .error(R.drawable.ic_image_loadfail).crossFade().into(imageView); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/utils/LogUtils.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.utils; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Description : 日子工具类 7 | * Author : lauren 8 | * Email : lauren.liuling@gmail.com 9 | * Blog : http://www.liuling123.com 10 | * Date : 15/12/14 11 | */ 12 | public class LogUtils { 13 | public static final boolean DEBUG = true; 14 | 15 | public static void v(String tag, String message) { 16 | if(DEBUG) { 17 | Log.v(tag, message); 18 | } 19 | } 20 | 21 | public static void d(String tag, String message) { 22 | if(DEBUG) { 23 | Log.d(tag, message); 24 | } 25 | } 26 | 27 | public static void i(String tag, String message) { 28 | if(DEBUG) { 29 | Log.i(tag, message); 30 | } 31 | } 32 | 33 | public static void w(String tag, String message) { 34 | if(DEBUG) { 35 | Log.w(tag, message); 36 | } 37 | } 38 | 39 | public static void e(String tag, String message) { 40 | if(DEBUG) { 41 | Log.e(tag, message); 42 | } 43 | } 44 | 45 | public static void e(String tag, String message, Exception e) { 46 | if(DEBUG) { 47 | Log.e(tag, message, e); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleNBA 2 | 基于Material Design和MVP的新闻客户端 3 | 4 | # Screenshot 5 | ###### NBA 6 | ![image](https://github.com/fuqinwu/SimpleNBA/blob/master/screenshot/nba.png) 7 |
8 | 9 | ###依赖的官方支持包 10 | * **com.android.support:support-v4:22.2.1** 11 | * **com.android.support:appcompat-v7:22.2.1** 12 | * **com.android.support:design:22.2.1** 13 | 14 | ### 引用库 15 | 16 | 库名称 | 库信息 17 | ------- | ------- 18 | [Gson](https://github.com/google/gson) | Google json库,使用了@SerializedName标签、转对象 19 | [okhttp](https://github.com/square/okhttp) | 网络请求库 20 | [circleimageview](https://github.com/hdodenhof/CircleImageView) | 图片操作 21 | [glide](https://github.com/bumptech/glide) | 图片加载库 22 | 23 | 24 | ### License 25 | 26 | ``` 27 | Copyright 2015 hansheng 28 | 29 | Licensed under the Apache License, Version 2.0 (the "License"); 30 | you may not use this file except in compliance with the License. 31 | You may obtain a copy of the License at 32 | 33 | http://www.apache.org/licenses/LICENSE-2.0 34 | 35 | Unless required by applicable law or agreed to in writing, software 36 | distributed under the License is distributed on an "AS IS" BASIS, 37 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 38 | See the License for the specific language governing permissions and 39 | limitations under the License. 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/presenter/NbaDetailPresenterImpl.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.presenter; 2 | 3 | import android.content.Context; 4 | 5 | import com.hansheng.simplenba.beans.NbaDetailBean; 6 | import com.hansheng.simplenba.nba.model.NbaModel; 7 | import com.hansheng.simplenba.nba.model.NbaModelImpl; 8 | import com.hansheng.simplenba.nba.view.NbaDetailView; 9 | 10 | /** 11 | * Created by hansheng on 2016/3/28. 12 | */ 13 | public class NbaDetailPresenterImpl implements NbaDetailPresenter, NbaModelImpl.OnLoadNewsDetailListener { 14 | 15 | private Context mContent; 16 | private NbaDetailView mNewsDetailView; 17 | private NbaModel mNewsModel; 18 | 19 | public NbaDetailPresenterImpl(Context mContent, NbaDetailView mNewsDetailView) { 20 | this.mContent = mContent; 21 | this.mNewsDetailView = mNewsDetailView; 22 | mNewsModel = new NbaModelImpl(); 23 | } 24 | 25 | @Override 26 | public void loadNewsDetail(final String docId) { 27 | mNewsDetailView.showProgress(); 28 | mNewsModel.loadNewsDetail(docId, this); 29 | } 30 | 31 | 32 | @Override 33 | public void onSuccess(NbaDetailBean newsDetailBean) { 34 | if(newsDetailBean != null) { 35 | mNewsDetailView.showNewsDetialContent(newsDetailBean.getContent()); 36 | } 37 | mNewsDetailView.hideProgress(); 38 | } 39 | 40 | @Override 41 | public void onFailure(String msg, Exception e) { 42 | mNewsDetailView.hideProgress(); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /swipeback/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackPreferenceActivity.java: -------------------------------------------------------------------------------- 1 | 2 | package me.imid.swipebacklayout.lib.app; 3 | 4 | import me.imid.swipebacklayout.lib.SwipeBackLayout; 5 | import android.os.Bundle; 6 | import android.preference.PreferenceActivity; 7 | import android.view.View; 8 | 9 | public class SwipeBackPreferenceActivity extends PreferenceActivity implements SwipeBackActivityBase { 10 | private SwipeBackActivityHelper mHelper; 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | mHelper = new SwipeBackActivityHelper(this); 16 | mHelper.onActivityCreate(); 17 | } 18 | 19 | @Override 20 | protected void onPostCreate(Bundle savedInstanceState) { 21 | super.onPostCreate(savedInstanceState); 22 | mHelper.onPostCreate(); 23 | } 24 | 25 | @Override 26 | public View findViewById(int id) { 27 | View v = super.findViewById(id); 28 | if (v == null && mHelper != null) 29 | return mHelper.findViewById(id); 30 | return v; 31 | } 32 | 33 | @Override 34 | public SwipeBackLayout getSwipeBackLayout() { 35 | return mHelper.getSwipeBackLayout(); 36 | } 37 | @Override 38 | public void setSwipeBackEnable(boolean enable) { 39 | getSwipeBackLayout().setEnableGesture(enable); 40 | } 41 | 42 | @Override 43 | public void scrollToFinishActivity() { 44 | getSwipeBackLayout().scrollToFinishActivity(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /swipeback/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackActivity.java: -------------------------------------------------------------------------------- 1 | 2 | package me.imid.swipebacklayout.lib.app; 3 | 4 | import android.os.Bundle; 5 | import android.support.v4.app.FragmentActivity; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | 9 | import me.imid.swipebacklayout.lib.SwipeBackLayout; 10 | import me.imid.swipebacklayout.lib.Utils; 11 | 12 | public class SwipeBackActivity extends AppCompatActivity implements SwipeBackActivityBase { 13 | private SwipeBackActivityHelper mHelper; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | mHelper = new SwipeBackActivityHelper(this); 19 | mHelper.onActivityCreate(); 20 | } 21 | 22 | @Override 23 | protected void onPostCreate(Bundle savedInstanceState) { 24 | super.onPostCreate(savedInstanceState); 25 | mHelper.onPostCreate(); 26 | } 27 | 28 | @Override 29 | public View findViewById(int id) { 30 | View v = super.findViewById(id); 31 | if (v == null && mHelper != null) 32 | return mHelper.findViewById(id); 33 | return v; 34 | } 35 | 36 | @Override 37 | public SwipeBackLayout getSwipeBackLayout() { 38 | return mHelper.getSwipeBackLayout(); 39 | } 40 | 41 | @Override 42 | public void setSwipeBackEnable(boolean enable) { 43 | getSwipeBackLayout().setEnableGesture(enable); 44 | } 45 | 46 | @Override 47 | public void scrollToFinishActivity() { 48 | Utils.convertActivityToTranslucent(this); 49 | getSwipeBackLayout().scrollToFinishActivity(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 15 | 21 | 22 | 29 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonSyntaxException; 6 | 7 | import java.lang.reflect.Type; 8 | 9 | /** 10 | * Description : Json转换工具类 11 | * Author : lauren 12 | * Email : lauren.liuling@gmail.com 13 | * Blog : http://www.liuling123.com 14 | * Date : 15/12/17 15 | */ 16 | public class JsonUtils { 17 | 18 | private static Gson mGson = new Gson(); 19 | 20 | /** 21 | * 将对象准换为json字符串 22 | * @param object 23 | * @param 24 | * @return 25 | */ 26 | public static String serialize(T object) { 27 | return mGson.toJson(object); 28 | } 29 | 30 | /** 31 | * 将json字符串转换为对象 32 | * @param json 33 | * @param clz 34 | * @param 35 | * @return 36 | */ 37 | public static T deserialize(String json, Class clz) throws JsonSyntaxException { 38 | return mGson.fromJson(json, clz); 39 | } 40 | 41 | /** 42 | * 将json对象转换为实体对象 43 | * @param json 44 | * @param clz 45 | * @param 46 | * @return 47 | * @throws JsonSyntaxException 48 | */ 49 | public static T deserialize(JsonObject json, Class clz) throws JsonSyntaxException { 50 | return mGson.fromJson(json, clz); 51 | } 52 | 53 | /** 54 | * 将json字符串转换为对象 55 | * @param json 56 | * @param type 57 | * @param 58 | * @return 59 | */ 60 | public static T deserialize(String json, Type type) throws JsonSyntaxException { 61 | return mGson.fromJson(json, type); 62 | } 63 | 64 | 65 | 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /swipeback/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackActivityHelper.java: -------------------------------------------------------------------------------- 1 | package me.imid.swipebacklayout.lib.app; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.ColorDrawable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | 9 | import me.imid.swipebacklayout.lib.SwipeBackLayout; 10 | import me.imid.swipebacklayout.lib.Utils; 11 | 12 | /** 13 | * @author Yrom 14 | */ 15 | public class SwipeBackActivityHelper { 16 | private Activity mActivity; 17 | 18 | private SwipeBackLayout mSwipeBackLayout; 19 | 20 | public SwipeBackActivityHelper(Activity activity) { 21 | mActivity = activity; 22 | } 23 | 24 | @SuppressWarnings("deprecation") 25 | public void onActivityCreate() { 26 | mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 27 | mActivity.getWindow().getDecorView().setBackgroundDrawable(null); 28 | mSwipeBackLayout = (SwipeBackLayout) LayoutInflater.from(mActivity).inflate( 29 | me.imid.swipebacklayout.lib.R.layout.swipeback_layout, null); 30 | mSwipeBackLayout.addSwipeListener(new SwipeBackLayout.SwipeListener() { 31 | @Override 32 | public void onScrollStateChange(int state, float scrollPercent) { 33 | } 34 | 35 | @Override 36 | public void onEdgeTouch(int edgeFlag) { 37 | Utils.convertActivityToTranslucent(mActivity); 38 | } 39 | 40 | @Override 41 | public void onScrollOverThreshold() { 42 | 43 | } 44 | }); 45 | } 46 | 47 | public void onPostCreate() { 48 | mSwipeBackLayout.attachToActivity(mActivity); 49 | } 50 | 51 | public View findViewById(int id) { 52 | if (mSwipeBackLayout != null) { 53 | return mSwipeBackLayout.findViewById(id); 54 | } 55 | return null; 56 | } 57 | 58 | public SwipeBackLayout getSwipeBackLayout() { 59 | return mSwipeBackLayout; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_news.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 23 | 24 | 29 | 30 | 38 | 39 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/presenter/NewsPresenterImpl.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.presenter; 2 | 3 | import com.hansheng.simplenba.beans.NbaBean; 4 | import com.hansheng.simplenba.commons.Urls; 5 | import com.hansheng.simplenba.nba.model.NbaModel; 6 | import com.hansheng.simplenba.nba.model.NbaModelImpl; 7 | import com.hansheng.simplenba.nba.view.NbaView; 8 | import com.hansheng.simplenba.nba.widget.NbasFragment; 9 | import com.hansheng.simplenba.utils.LogUtils; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Description : 15 | * Author : lauren 16 | * Email : lauren.liuling@gmail.com 17 | * Blog : http://www.liuling123.com 18 | * Date : 15/12/18 19 | */ 20 | public class NewsPresenterImpl implements NbaPresenter, NbaModelImpl.OnLoadNewsListListener { 21 | 22 | private static final String TAG = "NewsPresenterImpl"; 23 | 24 | private NbaView mNewsView; 25 | private NbaModel mNewsModel; 26 | 27 | public NewsPresenterImpl(NbaView newsView) { 28 | this.mNewsView = newsView; 29 | this.mNewsModel = new NbaModelImpl(); 30 | } 31 | 32 | @Override 33 | public void loadNews() { 34 | 35 | 36 | } 37 | 38 | @Override 39 | public void loadNews(String type,String bas) { 40 | 41 | String url = getUrl(type); 42 | LogUtils.d(TAG, url); 43 | System.out.println("url="+url.toString()); 44 | mNewsModel.loadNews(url,type,bas,this); 45 | } 46 | 47 | /** 48 | * 根据类别和页面索引创建url 49 | * @param type 50 | * @return 51 | */ 52 | private String getUrl(String type) { 53 | StringBuffer sb = new StringBuffer(); 54 | switch (type) { 55 | case NbasFragment.NEWS_TYPE_TOP: 56 | sb.append(Urls.NBA); 57 | break; 58 | case NbasFragment.NEWS_TYPE_NBA: 59 | sb.append(Urls.BLOG); 60 | break; 61 | default: 62 | break; 63 | } 64 | return sb.toString(); 65 | } 66 | 67 | 68 | 69 | @Override 70 | public void onSuccess(List list) { 71 | mNewsView.hideProgress(); 72 | mNewsView.addNews(list); 73 | } 74 | 75 | @Override 76 | public void onFailure(String msg, Exception e) { 77 | mNewsView.hideProgress(); 78 | mNewsView.showLoadFailMsg(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/NewsJsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonParser; 7 | import com.hansheng.simplenba.beans.NbaBean; 8 | import com.hansheng.simplenba.beans.NbaDetailBean; 9 | import com.hansheng.simplenba.beans.NextId; 10 | import com.hansheng.simplenba.utils.JsonUtils; 11 | import com.hansheng.simplenba.utils.LogUtils; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * hansheng 18 | */ 19 | public class NewsJsonUtils { 20 | 21 | private final static String TAG = "NewsJsonUtils"; 22 | 23 | /** 24 | * 将获取到的json转换为新闻列表对象 25 | * 26 | * @param res 27 | * @param 28 | * @return 29 | */ 30 | 31 | public static List readJsonNewsBeans(String res) { 32 | List beans = new ArrayList(); 33 | JsonParser parser = new JsonParser(); 34 | JsonObject jsonobj = parser.parse(res).getAsJsonObject(); 35 | JsonElement jsonElement = jsonobj.get("newslist"); 36 | if (jsonElement == null) { 37 | return null; 38 | } 39 | JsonArray jsonArray = jsonElement.getAsJsonArray(); 40 | for (int i = 1; i < jsonArray.size(); i++) { 41 | JsonObject jo = jsonArray.get(i).getAsJsonObject(); 42 | NbaBean news = JsonUtils.deserialize(jo, NbaBean.class); 43 | beans.add(news); 44 | } 45 | System.out.println("jsonobj="+jsonobj.get("nextId")); 46 | 47 | // System.out.println("jsonobj==" + jsonobj.toString()); 48 | return beans; 49 | } 50 | 51 | 52 | 53 | 54 | public static NbaDetailBean readJsonNewsDetailBeans(String res) { 55 | NbaDetailBean newsDetailBean = null; 56 | try { 57 | JsonParser parser = new JsonParser(); 58 | JsonObject jsonObj = parser.parse(res).getAsJsonObject(); 59 | System.out.println("jsonObj=" + jsonObj); 60 | newsDetailBean = JsonUtils.deserialize(parser.parse(res).getAsJsonObject(), NbaDetailBean.class); 61 | } catch (Exception e) { 62 | LogUtils.e(TAG, "readJsonNewsBeans error", e); 63 | } 64 | return newsDetailBean; 65 | } 66 | 67 | public static NextId readJsonNextId(String response) { 68 | 69 | NextId nextId = null; 70 | try { 71 | JsonParser parser = new JsonParser(); 72 | JsonObject jsonObj = parser.parse(response).getAsJsonObject(); 73 | System.out.println("jsonObj=" + jsonObj); 74 | nextId = JsonUtils.deserialize(parser.parse(response).getAsJsonObject(),NextId.class); 75 | } catch (Exception e) { 76 | LogUtils.e(TAG, "readJsonNewsBeans error", e); 77 | } 78 | return nextId; 79 | } 80 | } 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /swipeback/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | repositories { 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | compile 'com.android.support:appcompat-v7:23.1.1' 9 | } 10 | 11 | android { 12 | compileSdkVersion Integer.parseInt(ANDROID_BUILD_COMPILE_SDK_VERSION) 13 | buildToolsVersion ANDROID_BUILD_TOOLS_VERSION 14 | 15 | defaultConfig { 16 | minSdkVersion Integer.parseInt(MIN_SDK_VERSION) 17 | targetSdkVersion Integer.parseInt(ANDROID_BUILD_TARGET_SDK_VERSION) 18 | versionCode Integer.parseInt(VERSION_CODE) 19 | versionName VERSION_NAME 20 | } 21 | } 22 | 23 | /* 24 | def siteUrl = 'https://github.com/ikew0ng/SwipeBackLayout' 25 | def gitUrl = 'https://github.com/ikew0ng/SwipeBackLayout.git' 26 | group = "me.imid.swipebacklayout.lib" 27 | 28 | install { 29 | repositories.mavenInstaller { 30 | pom { 31 | project { 32 | packaging 'aar' 33 | // Add your description here 34 | name 'An Android library that help you to build app with swipe back gesture.' 35 | url siteUrl 36 | licenses { 37 | license { 38 | name 'The Apache Software License, Version 2.0' 39 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 40 | } 41 | } 42 | developers { 43 | developer { 44 | id 'ike' 45 | name 'ike' 46 | email 'ike@imid.me' 47 | } 48 | } 49 | scm { 50 | connection gitUrl 51 | developerConnection gitUrl 52 | url siteUrl 53 | } 54 | } 55 | } 56 | } 57 | } 58 | task sourcesJar(type: Jar) { 59 | from android.sourceSets.main.java.srcDirs 60 | classifier = 'sources' 61 | } 62 | task javadoc(type: Javadoc) { 63 | source = android.sourceSets.main.java.srcDirs 64 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 65 | } 66 | task javadocJar(type: Jar, dependsOn: javadoc) { 67 | classifier = 'javadoc' 68 | from javadoc.destinationDir 69 | } 70 | artifacts { 71 | archives javadocJar 72 | archives sourcesJar 73 | } 74 | Properties properties = new Properties() 75 | File gradle_properties = project.file('gradle.properties'); 76 | if (gradle_properties.exists()) { 77 | properties.load(project.file('gradle.properties').newDataInputStream()) 78 | } 79 | bintray { 80 | user = properties.getProperty("bintray.user") 81 | key = properties.getProperty("bintray.apikey") 82 | configurations = ['archives'] 83 | pkg { 84 | repo = "maven" 85 | name = "SwipeBackLayout" 86 | websiteUrl = siteUrl 87 | vcsUrl = gitUrl 88 | licenses = ["Apache-2.0"] 89 | publish = true 90 | } 91 | }*/ -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/model/NbaModelImpl.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.model; 2 | 3 | import com.hansheng.simplenba.beans.NbaBean; 4 | import com.hansheng.simplenba.beans.NbaDetailBean; 5 | import com.hansheng.simplenba.beans.NextId; 6 | import com.hansheng.simplenba.nba.NewsJsonUtils; 7 | import com.hansheng.simplenba.utils.OkHttpUtils; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by hansheng on 2016/3/26. 13 | */ 14 | public class NbaModelImpl implements NbaModel { 15 | 16 | /** 17 | * 加载新闻列表 18 | * @param url 19 | * @param listener 20 | */ 21 | @Override 22 | public void loadNews(String url,String type, String bas,final OnLoadNewsListListener listener) { 23 | OkHttpUtils.ResultCallback loadNewsCallback = new OkHttpUtils.ResultCallback() { 24 | @Override 25 | public void onSuccess(String response){ 26 | List newsBeanList = NewsJsonUtils.readJsonNewsBeans(response); 27 | NextId newsBean = NewsJsonUtils.readJsonNextId(response); 28 | System.out.println("newsBeanList="+newsBeanList); 29 | listener.onSuccess(newsBeanList); 30 | } 31 | 32 | @Override 33 | public void onFailure(Exception e) { 34 | listener.onFailure("load news list failure.", e); 35 | } 36 | }; 37 | System.out.println("url======"+url+bas); 38 | OkHttpUtils.get(url+bas, loadNewsCallback); 39 | } 40 | 41 | /** 42 | * 加载新闻详情 43 | * @param url 44 | * @param listener 45 | */ 46 | @Override 47 | public void loadNewsDetail(final String url, final OnLoadNewsDetailListener listener) { 48 | // String url = getDetailUrl(docid); 49 | OkHttpUtils.ResultCallback loadNewsCallback = new OkHttpUtils.ResultCallback() { 50 | @Override 51 | public void onSuccess(String response) { 52 | NbaDetailBean newsDetailBean = NewsJsonUtils.readJsonNewsDetailBeans(response); 53 | listener.onSuccess(newsDetailBean); 54 | } 55 | 56 | @Override 57 | public void onFailure(Exception e) { 58 | listener.onFailure("load news detail info failure.", e); 59 | } 60 | }; 61 | OkHttpUtils.get(url, loadNewsCallback); 62 | } 63 | 64 | 65 | 66 | // private String getDetailUrl(String docId) { 67 | // StringBuffer sb = new StringBuffer(Urls.NEW_DETAIL); 68 | // sb.append(docId).append(Urls.END_DETAIL_URL); 69 | // return sb.toString(); 70 | // } 71 | 72 | public interface OnLoadNewsListListener { 73 | void onSuccess(List list); 74 | void onFailure(String msg, Exception e); 75 | } 76 | 77 | public interface OnLoadNewsDetailListener { 78 | void onSuccess(NbaDetailBean newsDetailBean); 79 | void onFailure(String msg, Exception e); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_news_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 23 | 24 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 53 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/utils/ToolsUtil.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.utils; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | /** 8 | * Description : 9 | * Author : lauren 10 | * Email : lauren.liuling@gmail.com 11 | * Blog : http://www.liuling123.com 12 | * Date : 15/12/21 13 | */ 14 | public class ToolsUtil { 15 | 16 | public static final int getHeightInPx(Context context) { 17 | final int height = context.getResources().getDisplayMetrics().heightPixels; 18 | return height; 19 | } 20 | 21 | public static final int getWidthInPx(Context context) { 22 | final int width = context.getResources().getDisplayMetrics().widthPixels; 23 | return width; 24 | } 25 | 26 | public static final int getHeightInDp(Context context) { 27 | final float height = context.getResources().getDisplayMetrics().heightPixels; 28 | int heightInDp = px2dip(context, height); 29 | return heightInDp; 30 | } 31 | 32 | public static final int getWidthInDp(Context context) { 33 | final float width = context.getResources().getDisplayMetrics().widthPixels; 34 | int widthInDp = px2dip(context, width); 35 | return widthInDp; 36 | } 37 | 38 | public static int dip2px(Context context, float dpValue) { 39 | final float scale = context.getResources().getDisplayMetrics().density; 40 | return (int) (dpValue * scale + 0.5f); 41 | } 42 | 43 | public static int px2dip(Context context, float pxValue) { 44 | final float scale = context.getResources().getDisplayMetrics().density; 45 | return (int) (pxValue / scale + 0.5f); 46 | } 47 | 48 | public static int px2sp(Context context, float pxValue) { 49 | final float scale = context.getResources().getDisplayMetrics().density; 50 | return (int) (pxValue / scale + 0.5f); 51 | } 52 | 53 | public static int sp2px(Context context, float spValue) { 54 | final float scale = context.getResources().getDisplayMetrics().density; 55 | return (int) (spValue * scale + 0.5f); 56 | } 57 | 58 | /** 59 | * 获得状态栏的高度 60 | * 61 | * @param context 62 | * @return 63 | */ 64 | public static int getStatusHeight(Context context) { 65 | 66 | int statusHeight = -1; 67 | try { 68 | Class clazz = Class.forName("com.android.internal.R$dimen"); 69 | Object object = clazz.newInstance(); 70 | int height = Integer.parseInt(clazz.getField("status_bar_height") 71 | .get(object).toString()); 72 | statusHeight = context.getResources().getDimensionPixelSize(height); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | } 76 | return statusHeight; 77 | } 78 | 79 | /** 80 | * 判断网络是否可用 81 | * @param context 82 | * @return 83 | */ 84 | public static boolean isNetworkAvailable(Context context) { 85 | ConnectivityManager cm = (ConnectivityManager) context 86 | .getSystemService(Context.CONNECTIVITY_SERVICE); 87 | if (cm != null) { 88 | //如果仅仅是用来判断网络连接 89 | //则可以使用 cm.getActiveNetworkInfo().isAvailable(); 90 | NetworkInfo[] info = cm.getAllNetworkInfo(); 91 | if (info != null) { 92 | for (int i = 0; i < info.length; i++) { 93 | if (info[i].getState() == NetworkInfo.State.CONNECTED) { 94 | return true; 95 | } 96 | } 97 | } 98 | } 99 | return false; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/widget/NbasFragment.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.widget; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.design.widget.TabLayout; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v4.app.FragmentPagerAdapter; 9 | import android.support.v4.view.ViewPager; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import com.hansheng.simplenba.R; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * Created by hansheng on 2016/3/26. 21 | */ 22 | public class NbasFragment extends Fragment { 23 | 24 | 25 | public static final String NEWS_TYPE_TOP = "0"; 26 | public static final String NEWS_TYPE_NBA = "1"; 27 | public static final int NEWS_TYPE_CARS = 2; 28 | public static final int NEWS_TYPE_JOKES = 3; 29 | 30 | private TabLayout mTablayout; 31 | private ViewPager mViewPager; 32 | 33 | @Nullable 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 | View view = inflater.inflate(R.layout.content_main, null); 37 | mTablayout = (TabLayout) view.findViewById(R.id.tab_layout); 38 | mViewPager = (ViewPager) view.findViewById(R.id.viewpager); 39 | mViewPager.setOffscreenPageLimit(3); 40 | setupViewPager(mViewPager); 41 | mTablayout.addTab(mTablayout.newTab().setText(R.string.top)); 42 | mTablayout.addTab(mTablayout.newTab().setText(R.string.nba)); 43 | // mTablayout.addTab(mTablayout.newTab().setText(R.string.cars)); 44 | // mTablayout.addTab(mTablayout.newTab().setText(R.string.jokes)); 45 | mTablayout.setupWithViewPager(mViewPager); 46 | return view; 47 | } 48 | 49 | private void setupViewPager(ViewPager mViewPager) { 50 | //Fragment中嵌套使用Fragment一定要使用getChildFragmentManager(),否则会有问题 51 | MyPagerAdapter adapter = new MyPagerAdapter(getChildFragmentManager()); 52 | adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_TOP), getString(R.string.top)); 53 | adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_NBA), getString(R.string.nba)); 54 | // adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_CARS), getString(R.string.cars)); 55 | // adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_JOKES), getString(R.string.jokes)); 56 | mViewPager.setAdapter(adapter); 57 | } 58 | 59 | public static class MyPagerAdapter extends FragmentPagerAdapter { 60 | private final List mFragments = new ArrayList<>(); 61 | private final List mFragmentTitles = new ArrayList<>(); 62 | 63 | public MyPagerAdapter(FragmentManager fm) { 64 | super(fm); 65 | } 66 | 67 | public void addFragment(Fragment fragment, String title) { 68 | mFragments.add(fragment); 69 | mFragmentTitles.add(title); 70 | } 71 | 72 | @Override 73 | public Fragment getItem(int position) { 74 | return mFragments.get(position); 75 | } 76 | 77 | @Override 78 | public int getCount() { 79 | return mFragments.size(); 80 | } 81 | 82 | @Override 83 | public CharSequence getPageTitle(int position) { 84 | return mFragmentTitles.get(position); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/beans/NbaBean.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.beans; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by hansheng on 2016/3/26. 8 | */ 9 | public class NbaBean implements Serializable { 10 | 11 | 12 | /** 13 | * contentType : ARTICLE 14 | * description : 北京时间3月26日,在湖人主场对阵掘金的比赛中,榜眼丹吉洛-拉塞尔右脚踝扭伤,被搀扶离场。他在受伤后 15 | * title : 拉塞尔脚踝扭伤 万幸骨头跟腱没问题 16 | * putdate : 1458972000000 17 | * imgUrlList : ["http://image.res.meizu.com/image/reader/215c90abac25f0f7edaa006e5d70a947/original"] 18 | * randomNum : 1458971777000 19 | * articleId : 117677376 20 | * contentSourceName : NBA 21 | * articleUrl : http://reader.res.meizu.com/reader/articlecontent/20160326/117677376.json 22 | * type : IMAGETEXT 23 | * sourceType : ZAKER 24 | */ 25 | 26 | 27 | 28 | private String contentType; 29 | private String description; 30 | private String title; 31 | private long putdate; 32 | private long randomNum; 33 | private int articleId; 34 | private String contentSourceName; 35 | private String articleUrl; 36 | private String type; 37 | private String sourceType; 38 | private List imgUrlList; 39 | 40 | 41 | 42 | public void setContentType(String contentType) { 43 | this.contentType = contentType; 44 | } 45 | 46 | public void setDescription(String description) { 47 | this.description = description; 48 | } 49 | 50 | public void setTitle(String title) { 51 | this.title = title; 52 | } 53 | 54 | public void setPutdate(long putdate) { 55 | this.putdate = putdate; 56 | } 57 | 58 | public void setRandomNum(long randomNum) { 59 | this.randomNum = randomNum; 60 | } 61 | 62 | public void setArticleId(int articleId) { 63 | this.articleId = articleId; 64 | } 65 | 66 | public void setContentSourceName(String contentSourceName) { 67 | this.contentSourceName = contentSourceName; 68 | } 69 | 70 | public void setArticleUrl(String articleUrl) { 71 | this.articleUrl = articleUrl; 72 | } 73 | 74 | public void setType(String type) { 75 | this.type = type; 76 | } 77 | 78 | public void setSourceType(String sourceType) { 79 | this.sourceType = sourceType; 80 | } 81 | 82 | public void setImgUrlList(List imgUrlList) { 83 | this.imgUrlList = imgUrlList; 84 | } 85 | 86 | public String getContentType() { 87 | return contentType; 88 | } 89 | 90 | public String getDescription() { 91 | return description; 92 | } 93 | 94 | public String getTitle() { 95 | return title; 96 | } 97 | 98 | public long getPutdate() { 99 | return putdate; 100 | } 101 | 102 | public long getRandomNum() { 103 | return randomNum; 104 | } 105 | 106 | public int getArticleId() { 107 | return articleId; 108 | } 109 | 110 | public String getContentSourceName() { 111 | return contentSourceName; 112 | } 113 | 114 | public String getArticleUrl() { 115 | return articleUrl; 116 | } 117 | 118 | public String getType() { 119 | return type; 120 | } 121 | 122 | public String getSourceType() { 123 | return sourceType; 124 | } 125 | 126 | public List getImgUrlList() { 127 | return imgUrlList; 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/widget/NbaDetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.widget; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.CollapsingToolbarLayout; 5 | import android.support.v7.widget.Toolbar; 6 | import android.view.View; 7 | import android.widget.ImageView; 8 | import android.widget.ProgressBar; 9 | 10 | import com.hansheng.simplenba.R; 11 | import com.hansheng.simplenba.beans.NbaBean; 12 | import com.hansheng.simplenba.nba.presenter.NbaDetailPresenter; 13 | import com.hansheng.simplenba.nba.presenter.NbaDetailPresenterImpl; 14 | import com.hansheng.simplenba.nba.view.NbaDetailView; 15 | import com.hansheng.simplenba.utils.ImageLoaderUtils; 16 | import com.hansheng.simplenba.utils.ToolsUtil; 17 | 18 | import org.sufficientlysecure.htmltextview.HtmlTextView; 19 | 20 | import java.util.List; 21 | 22 | import me.imid.swipebacklayout.lib.SwipeBackLayout; 23 | import me.imid.swipebacklayout.lib.app.SwipeBackActivity; 24 | 25 | /** 26 | * Created by hansheng on 2016/3/28. 27 | */ 28 | public class NbaDetailActivity extends SwipeBackActivity implements NbaDetailView { 29 | 30 | private NbaBean mNews; 31 | private HtmlTextView mTVNewsContent; 32 | private NbaDetailPresenter mNewsDetailPresenter; 33 | private ProgressBar mProgressBar; 34 | private SwipeBackLayout mSwipeBackLayout; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_news_detail); 40 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 41 | mProgressBar = (ProgressBar) findViewById(R.id.progress); 42 | mTVNewsContent = (HtmlTextView) findViewById(R.id.htNewsContent); 43 | 44 | setSupportActionBar(toolbar); 45 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 46 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 47 | @Override 48 | public void onClick(View view) { 49 | onBackPressed(); 50 | } 51 | }); 52 | 53 | mSwipeBackLayout = getSwipeBackLayout(); 54 | mSwipeBackLayout.setEdgeSize(ToolsUtil.getWidthInPx(this)); 55 | mSwipeBackLayout.setEdgeTrackingEnabled(SwipeBackLayout.EDGE_LEFT); 56 | 57 | mNews = (NbaBean) getIntent().getSerializableExtra("news"); 58 | 59 | CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); 60 | collapsingToolbar.setTitle(mNews.getTitle()); 61 | 62 | 63 | List img= mNews.getImgUrlList(); 64 | if(img.isEmpty()){ 65 | return; 66 | } 67 | else{ 68 | System.out.println("img========"+img.get(0)+ mNews.getTitle()+mNews.getDescription()+mNews.getImgUrlList().toString()); 69 | ImageLoaderUtils.display(getApplicationContext(), (ImageView) findViewById(R.id.ivImage), img.get(0)); 70 | } 71 | 72 | 73 | mNewsDetailPresenter = new NbaDetailPresenterImpl(getApplication(), this); 74 | mNewsDetailPresenter.loadNewsDetail(mNews.getArticleUrl()); 75 | } 76 | 77 | @Override 78 | public void showNewsDetialContent(String newsDetailContent) { 79 | mTVNewsContent.setHtmlFromString(newsDetailContent, new HtmlTextView.LocalImageGetter()); 80 | } 81 | 82 | @Override 83 | public void showProgress() { 84 | mProgressBar.setVisibility(View.VISIBLE); 85 | } 86 | 87 | @Override 88 | public void hideProgress() { 89 | mProgressBar.setVisibility(View.GONE); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/widget/DividerItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.View; 11 | 12 | /* 13 | * Copyright (C) 2014 The Android Open Source Project 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | public class DividerItemDecoration extends RecyclerView.ItemDecoration { 28 | 29 | private static final int[] ATTRS = new int[]{ 30 | android.R.attr.listDivider 31 | }; 32 | 33 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; 34 | 35 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; 36 | 37 | private Drawable mDivider; 38 | 39 | private int mOrientation; 40 | 41 | public DividerItemDecoration(Context context, int orientation) { 42 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 43 | mDivider = a.getDrawable(0); 44 | a.recycle(); 45 | setOrientation(orientation); 46 | } 47 | 48 | public void setOrientation(int orientation) { 49 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { 50 | throw new IllegalArgumentException("invalid orientation"); 51 | } 52 | mOrientation = orientation; 53 | } 54 | 55 | @Override 56 | public void onDraw(Canvas c, RecyclerView parent) { 57 | if (mOrientation == VERTICAL_LIST) { 58 | drawVertical(c, parent); 59 | } else { 60 | drawHorizontal(c, parent); 61 | } 62 | } 63 | 64 | public void drawVertical(Canvas c, RecyclerView parent) { 65 | final int left = parent.getPaddingLeft(); 66 | final int right = parent.getWidth() - parent.getPaddingRight(); 67 | 68 | final int childCount = parent.getChildCount(); 69 | for (int i = 0; i < childCount; i++) { 70 | final View child = parent.getChildAt(i); 71 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 72 | .getLayoutParams(); 73 | final int top = child.getBottom() + params.bottomMargin; 74 | final int bottom = top + mDivider.getIntrinsicHeight(); 75 | mDivider.setBounds(left, top, right, bottom); 76 | mDivider.draw(c); 77 | } 78 | } 79 | 80 | public void drawHorizontal(Canvas c, RecyclerView parent) { 81 | final int top = parent.getPaddingTop(); 82 | final int bottom = parent.getHeight() - parent.getPaddingBottom(); 83 | 84 | final int childCount = parent.getChildCount(); 85 | for (int i = 0; i < childCount; i++) { 86 | final View child = parent.getChildAt(i); 87 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 88 | .getLayoutParams(); 89 | final int left = child.getRight() + params.rightMargin; 90 | final int right = left + mDivider.getIntrinsicHeight(); 91 | mDivider.setBounds(left, top, right, bottom); 92 | mDivider.draw(c); 93 | } 94 | } 95 | 96 | @Override 97 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { 98 | if (mOrientation == VERTICAL_LIST) { 99 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 100 | } else { 101 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /swipeback/src/main/java/me/imid/swipebacklayout/lib/Utils.java: -------------------------------------------------------------------------------- 1 | 2 | package me.imid.swipebacklayout.lib; 3 | 4 | import android.app.Activity; 5 | import android.app.ActivityOptions; 6 | import android.os.Build; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Created by Chaojun Wang on 6/9/14. 12 | */ 13 | public class Utils { 14 | private Utils() { 15 | } 16 | 17 | /** 18 | * Convert a translucent themed Activity 19 | * {@link android.R.attr#windowIsTranslucent} to a fullscreen opaque 20 | * Activity. 21 | *

22 | * Call this whenever the background of a translucent Activity has changed 23 | * to become opaque. Doing so will allow the {@link android.view.Surface} of 24 | * the Activity behind to be released. 25 | *

26 | * This call has no effect on non-translucent activities or on activities 27 | * with the {@link android.R.attr#windowIsFloating} attribute. 28 | */ 29 | public static void convertActivityFromTranslucent(Activity activity) { 30 | try { 31 | Method method = Activity.class.getDeclaredMethod("convertFromTranslucent"); 32 | method.setAccessible(true); 33 | method.invoke(activity); 34 | } catch (Throwable t) { 35 | } 36 | } 37 | 38 | /** 39 | * Convert a translucent themed Activity 40 | * {@link android.R.attr#windowIsTranslucent} back from opaque to 41 | * translucent following a call to 42 | * {@link #convertActivityFromTranslucent(android.app.Activity)} . 43 | *

44 | * Calling this allows the Activity behind this one to be seen again. Once 45 | * all such Activities have been redrawn 46 | *

47 | * This call has no effect on non-translucent activities or on activities 48 | * with the {@link android.R.attr#windowIsFloating} attribute. 49 | */ 50 | public static void convertActivityToTranslucent(Activity activity) { 51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 52 | convertActivityToTranslucentAfterL(activity); 53 | } else { 54 | convertActivityToTranslucentBeforeL(activity); 55 | } 56 | } 57 | 58 | /** 59 | * Calling the convertToTranslucent method on platforms before Android 5.0 60 | */ 61 | public static void convertActivityToTranslucentBeforeL(Activity activity) { 62 | try { 63 | Class[] classes = Activity.class.getDeclaredClasses(); 64 | Class translucentConversionListenerClazz = null; 65 | for (Class clazz : classes) { 66 | if (clazz.getSimpleName().contains("TranslucentConversionListener")) { 67 | translucentConversionListenerClazz = clazz; 68 | } 69 | } 70 | Method method = Activity.class.getDeclaredMethod("convertToTranslucent", 71 | translucentConversionListenerClazz); 72 | method.setAccessible(true); 73 | method.invoke(activity, new Object[] { 74 | null 75 | }); 76 | } catch (Throwable t) { 77 | } 78 | } 79 | 80 | /** 81 | * Calling the convertToTranslucent method on platforms after Android 5.0 82 | */ 83 | private static void convertActivityToTranslucentAfterL(Activity activity) { 84 | try { 85 | Method getActivityOptions = Activity.class.getDeclaredMethod("getActivityOptions"); 86 | getActivityOptions.setAccessible(true); 87 | Object options = getActivityOptions.invoke(activity); 88 | 89 | Class[] classes = Activity.class.getDeclaredClasses(); 90 | Class translucentConversionListenerClazz = null; 91 | for (Class clazz : classes) { 92 | if (clazz.getSimpleName().contains("TranslucentConversionListener")) { 93 | translucentConversionListenerClazz = clazz; 94 | } 95 | } 96 | Method convertToTranslucent = Activity.class.getDeclaredMethod("convertToTranslucent", 97 | translucentConversionListenerClazz, ActivityOptions.class); 98 | convertToTranslucent.setAccessible(true); 99 | convertToTranslucent.invoke(activity, null, options); 100 | } catch (Throwable t) { 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v4.app.FragmentPagerAdapter; 8 | import android.support.v4.view.ViewPager; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.support.v7.widget.Toolbar; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | 14 | import com.hansheng.simplenba.nba.widget.NbasFragment; 15 | import com.hansheng.simplenba.nba.widget.NewListFragment; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class MainActivity extends AppCompatActivity { 21 | public static final String NEWS_TYPE_TOP ="0"; 22 | public static final String NEWS_TYPE_NBA = "1"; 23 | public static final int NEWS_TYPE_CARS = 2; 24 | public static final int NEWS_TYPE_JOKES = 3; 25 | private TabLayout mTablayout; 26 | private ViewPager mViewPager; 27 | 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_main); 33 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 34 | setSupportActionBar(toolbar); 35 | mTablayout = (TabLayout) findViewById(R.id.tab_layout); 36 | mViewPager = (ViewPager) findViewById(R.id.viewpager); 37 | mViewPager.setOffscreenPageLimit(3); 38 | setupViewPager(mViewPager); 39 | mTablayout.addTab(mTablayout.newTab().setText(R.string.top)); 40 | mTablayout.addTab(mTablayout.newTab().setText(R.string.nba)); 41 | // mTablayout.addTab(mTablayout.newTab().setText(R.string.cars)); 42 | // mTablayout.addTab(mTablayout.newTab().setText(R.string.jokes)); 43 | mTablayout.setupWithViewPager(mViewPager); 44 | new NbasFragment(); 45 | 46 | } 47 | 48 | private void setupViewPager(ViewPager mViewPager) { 49 | 50 | MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager()); 51 | adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_TOP), getString(R.string.top)); 52 | adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_NBA), getString(R.string.nba)); 53 | // adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_CARS), getString(R.string.cars)); 54 | // adapter.addFragment(NewListFragment.newInstance(NEWS_TYPE_JOKES), getString(R.string.jokes)); 55 | mViewPager.setAdapter(adapter); 56 | } 57 | 58 | private class MyPagerAdapter extends FragmentPagerAdapter { 59 | private final List mFragments = new ArrayList<>(); 60 | private final List mFragmentTitles = new ArrayList<>(); 61 | 62 | public MyPagerAdapter(FragmentManager fm) { 63 | super(fm); 64 | } 65 | 66 | // public MyPagerAdapter(FragmentManager fm) { 67 | // super(fm); 68 | // } 69 | public void addFragment(Fragment fragment, String title) { 70 | mFragments.add(fragment); 71 | mFragmentTitles.add(title); 72 | } 73 | 74 | @Override 75 | public Fragment getItem(int position) { 76 | return mFragments.get(position); 77 | } 78 | 79 | @Override 80 | public int getCount() { 81 | return mFragments.size(); 82 | } 83 | } 84 | 85 | 86 | 87 | @Override 88 | public boolean onCreateOptionsMenu(Menu menu) { 89 | // Inflate the menu; this adds items to the action bar if it is present. 90 | getMenuInflater().inflate(R.menu.menu_main, menu); 91 | return true; 92 | } 93 | 94 | @Override 95 | public boolean onOptionsItemSelected(MenuItem item) { 96 | // Handle action bar item clicks here. The action bar will 97 | // automatically handle clicks on the Home/Up button, so long 98 | // as you specify a parent activity in AndroidManifest.xml. 99 | int id = item.getItemId(); 100 | 101 | //noinspection SimplifiableIfStatement 102 | if (id == R.id.action_settings) { 103 | return true; 104 | } 105 | 106 | return super.onOptionsItemSelected(item); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/NbaAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.hansheng.simplenba.R; 12 | import com.hansheng.simplenba.beans.NbaBean; 13 | import com.hansheng.simplenba.utils.ImageLoaderUtils; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * Created by hansheng on 2016/3/26. 19 | */ 20 | public class NbaAdapter extends RecyclerView.Adapter { 21 | private static final int TYPE_ITEM = 0; 22 | private static final int TYPE_FOOTER = 1; 23 | 24 | private List mData; 25 | private boolean mShowFooter = true; 26 | private Context mContext; 27 | 28 | private OnItemClickListener mOnItemClickListener; 29 | 30 | public NbaAdapter(Context context) { 31 | this.mContext = context; 32 | } 33 | 34 | public void setmDate(List data) { 35 | this.mData = data; 36 | System.out.println("mdata=="+mData); 37 | this.notifyDataSetChanged(); 38 | } 39 | 40 | @Override 41 | public int getItemViewType(int position) { 42 | // 最后一个item设置为footerView 43 | if(!mShowFooter) { 44 | return TYPE_ITEM; 45 | } 46 | if (position + 1 == getItemCount()) { 47 | return TYPE_FOOTER; 48 | } else { 49 | return TYPE_ITEM; 50 | } 51 | } 52 | 53 | @Override 54 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, 55 | int viewType) { 56 | if(viewType == TYPE_ITEM) { 57 | View v = LayoutInflater.from(parent.getContext()) 58 | .inflate(R.layout.item_news, parent, false); 59 | ItemViewHolder vh = new ItemViewHolder(v); 60 | return vh; 61 | } else { 62 | View view = LayoutInflater.from(parent.getContext()).inflate( 63 | R.layout.footer, null); 64 | view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 65 | ViewGroup.LayoutParams.WRAP_CONTENT)); 66 | return new FooterViewHolder(view); 67 | } 68 | } 69 | 70 | @Override 71 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 72 | if(holder instanceof ItemViewHolder) { 73 | 74 | NbaBean news = mData.get(position); 75 | if(news == null) { 76 | return; 77 | } 78 | ((ItemViewHolder) holder).mTitle.setText(news.getTitle()); 79 | ((ItemViewHolder) holder).mDesc.setText(news.getDescription()); 80 | // Uri uri = Uri.parse(news.getImgsrc()); 81 | // ((ItemViewHolder) holder).mNewsImg.setImageURI(uri); 82 | List img=news.getImgUrlList(); 83 | if(img.isEmpty()){ 84 | return; 85 | } 86 | else{ 87 | System.out.println("img========"+img.get(0)+news.getTitle()+news.getDescription()+news.getImgUrlList().toString()); 88 | ImageLoaderUtils.display(mContext, ((ItemViewHolder) holder).mNewsImg,img.get(0)); 89 | } 90 | 91 | } 92 | } 93 | 94 | @Override 95 | public int getItemCount() { 96 | int begin = mShowFooter?1:0; 97 | if(mData == null) { 98 | return begin; 99 | } 100 | return mData.size() + begin; 101 | } 102 | 103 | public NbaBean getItem(int position) { 104 | return mData == null ? null : mData.get(position); 105 | } 106 | 107 | public void isShowFooter(boolean showFooter) { 108 | this.mShowFooter = showFooter; 109 | } 110 | 111 | public boolean isShowFooter() { 112 | return this.mShowFooter; 113 | } 114 | 115 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 116 | this.mOnItemClickListener = onItemClickListener; 117 | } 118 | 119 | public class FooterViewHolder extends RecyclerView.ViewHolder { 120 | 121 | public FooterViewHolder(View view) { 122 | super(view); 123 | } 124 | 125 | } 126 | 127 | public interface OnItemClickListener { 128 | public void onItemClick(View view, int position); 129 | } 130 | 131 | public class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 132 | 133 | public TextView mTitle; 134 | public TextView mDesc; 135 | public ImageView mNewsImg; 136 | 137 | public ItemViewHolder(View v) { 138 | super(v); 139 | mTitle = (TextView) v.findViewById(R.id.tvTitle); 140 | mDesc = (TextView) v.findViewById(R.id.tvDesc); 141 | mNewsImg = (ImageView) v.findViewById(R.id.ivNews); 142 | v.setOnClickListener(this); 143 | } 144 | 145 | @Override 146 | public void onClick(View view) { 147 | if(mOnItemClickListener != null) { 148 | mOnItemClickListener.onItemClick(view, this.getPosition()); 149 | } 150 | } 151 | } 152 | 153 | } 154 | 155 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/utils/OkHttpUtils.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.utils; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import com.google.gson.internal.$Gson$Types; 7 | import com.squareup.okhttp.Callback; 8 | import com.squareup.okhttp.FormEncodingBuilder; 9 | import com.squareup.okhttp.OkHttpClient; 10 | import com.squareup.okhttp.Request; 11 | import com.squareup.okhttp.RequestBody; 12 | import com.squareup.okhttp.Response; 13 | 14 | import java.io.IOException; 15 | import java.lang.reflect.ParameterizedType; 16 | import java.lang.reflect.Type; 17 | import java.net.CookieManager; 18 | import java.net.CookiePolicy; 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * Description : OkHttp网络连接封装工具类 24 | * Author : lauren 25 | * Email : lauren.liuling@gmail.com 26 | * Blog : http://www.liuling123.com 27 | * Date : 15/12/17 28 | */ 29 | public class OkHttpUtils { 30 | 31 | private static final String TAG = "OkHttpUtils"; 32 | 33 | private static OkHttpUtils mInstance; 34 | private OkHttpClient mOkHttpClient; 35 | private Handler mDelivery; 36 | 37 | private OkHttpUtils() { 38 | mOkHttpClient = new OkHttpClient(); 39 | mOkHttpClient.setConnectTimeout(10, TimeUnit.SECONDS); 40 | mOkHttpClient.setWriteTimeout(10, TimeUnit.SECONDS); 41 | mOkHttpClient.setReadTimeout(30, TimeUnit.SECONDS); 42 | //cookie enabled 43 | mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)); 44 | mDelivery = new Handler(Looper.getMainLooper()); 45 | } 46 | 47 | private synchronized static OkHttpUtils getmInstance() { 48 | if (mInstance == null) { 49 | mInstance = new OkHttpUtils(); 50 | } 51 | return mInstance; 52 | } 53 | 54 | private void getRequest(String url, final ResultCallback callback) { 55 | final Request request = new Request.Builder().url(url).build(); 56 | deliveryResult(callback, request); 57 | } 58 | 59 | private void postRequest(String url, final ResultCallback callback, List params) { 60 | Request request = buildPostRequest(url, params); 61 | deliveryResult(callback, request); 62 | } 63 | 64 | private void deliveryResult(final ResultCallback callback, Request request) { 65 | 66 | mOkHttpClient.newCall(request).enqueue(new Callback() { 67 | @Override 68 | public void onFailure(Request request, final IOException e) { 69 | sendFailCallback(callback, e); 70 | } 71 | 72 | @Override 73 | public void onResponse(Response response) { 74 | try { 75 | String str = response.body().string(); 76 | if (callback.mType == String.class) { 77 | sendSuccessCallBack(callback, str); 78 | } else { 79 | Object object = JsonUtils.deserialize(str, callback.mType); 80 | sendSuccessCallBack(callback, object); 81 | } 82 | } catch (final Exception e) { 83 | LogUtils.e(TAG, "convert json failure", e); 84 | sendFailCallback(callback, e); 85 | } 86 | 87 | } 88 | }); 89 | } 90 | 91 | private void sendFailCallback(final ResultCallback callback, final Exception e) { 92 | mDelivery.post(new Runnable() { 93 | @Override 94 | public void run() { 95 | if (callback != null) { 96 | callback.onFailure(e); 97 | } 98 | } 99 | }); 100 | } 101 | 102 | private void sendSuccessCallBack(final ResultCallback callback, final Object obj) { 103 | mDelivery.post(new Runnable() { 104 | @Override 105 | public void run() { 106 | if (callback != null) { 107 | callback.onSuccess(obj); 108 | } 109 | } 110 | }); 111 | } 112 | 113 | private Request buildPostRequest(String url, List params) { 114 | FormEncodingBuilder builder = new FormEncodingBuilder(); 115 | for (Param param : params) { 116 | builder.add(param.key, param.value); 117 | } 118 | RequestBody requestBody = builder.build(); 119 | return new Request.Builder().url(url).post(requestBody).build(); 120 | } 121 | 122 | 123 | /**********************对外接口************************/ 124 | 125 | /** 126 | * get请求 127 | * @param url 请求url 128 | * @param callback 请求回调 129 | */ 130 | public static void get(String url, ResultCallback callback) { 131 | getmInstance().getRequest(url, callback); 132 | } 133 | 134 | /** 135 | * post请求 136 | * @param url 请求url 137 | * @param callback 请求回调 138 | * @param params 请求参数 139 | */ 140 | public static void post(String url, final ResultCallback callback, List params) { 141 | getmInstance().postRequest(url, callback, params); 142 | } 143 | 144 | /** 145 | * http请求回调类,回调方法在UI线程中执行 146 | * @param 147 | */ 148 | public static abstract class ResultCallback { 149 | 150 | Type mType; 151 | 152 | public ResultCallback(){ 153 | mType = getSuperclassTypeParameter(getClass()); 154 | } 155 | 156 | static Type getSuperclassTypeParameter(Class subclass) { 157 | Type superclass = subclass.getGenericSuperclass(); 158 | if (superclass instanceof Class) { 159 | throw new RuntimeException("Missing type parameter."); 160 | } 161 | ParameterizedType parameterized = (ParameterizedType) superclass; 162 | return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); 163 | } 164 | 165 | /** 166 | * 请求成功回调 167 | * @param response 168 | */ 169 | public abstract void onSuccess(T response); 170 | 171 | /** 172 | * 请求失败回调 173 | * @param e 174 | */ 175 | public abstract void onFailure(Exception e); 176 | } 177 | 178 | /** 179 | * post请求参数类 180 | */ 181 | public static class Param { 182 | 183 | String key; 184 | String value; 185 | 186 | public Param() { 187 | } 188 | 189 | public Param(String key, String value) { 190 | this.key = key; 191 | this.value = value; 192 | } 193 | 194 | } 195 | 196 | 197 | } 198 | -------------------------------------------------------------------------------- /app/src/main/java/com/hansheng/simplenba/nba/widget/NewListFragment.java: -------------------------------------------------------------------------------- 1 | package com.hansheng.simplenba.nba.widget; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.ActivityCompat; 7 | import android.support.v4.app.ActivityOptionsCompat; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v4.widget.SwipeRefreshLayout; 10 | import android.support.v7.widget.DefaultItemAnimator; 11 | import android.support.v7.widget.LinearLayoutManager; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | 17 | import com.hansheng.simplenba.R; 18 | import com.hansheng.simplenba.beans.NbaBean; 19 | import com.hansheng.simplenba.beans.NextId; 20 | import com.hansheng.simplenba.nba.NbaAdapter; 21 | import com.hansheng.simplenba.nba.presenter.NbaPresenter; 22 | import com.hansheng.simplenba.nba.presenter.NewsPresenterImpl; 23 | import com.hansheng.simplenba.nba.view.NbaView; 24 | import com.hansheng.simplenba.utils.LogUtils; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | /** 30 | * Created by hansheng on 2016/3/26. 31 | */ 32 | public class NewListFragment extends Fragment implements NbaView, SwipeRefreshLayout.OnRefreshListener { 33 | 34 | private static final String TAG = "NewsListFragment"; 35 | 36 | private SwipeRefreshLayout mSwipeRefreshWidget; 37 | private RecyclerView mRecyclerView; 38 | private LinearLayoutManager mLayoutManager; 39 | private NbaAdapter mAdapter; 40 | private List mData; 41 | private NbaPresenter mNewsPresenter; 42 | private NextId next= new NextId(); 43 | 44 | private String mType = NbasFragment.NEWS_TYPE_TOP; 45 | private int pageIndex = 0; 46 | 47 | public static NewListFragment newInstance(String type) { 48 | Bundle args = new Bundle(); 49 | NewListFragment fragment = new NewListFragment(); 50 | args.putString("type", type); 51 | fragment.setArguments(args); 52 | return fragment; 53 | } 54 | 55 | @Override 56 | public void onCreate(@Nullable Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | mNewsPresenter = new NewsPresenterImpl(this); 59 | mType = getArguments().getString("type"); 60 | System.out.println("mType=="+mType); 61 | } 62 | 63 | @Nullable 64 | @Override 65 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 66 | View view = inflater.inflate(R.layout.fragment_newslist, null); 67 | 68 | mSwipeRefreshWidget = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_widget); 69 | mSwipeRefreshWidget.setColorSchemeResources(R.color.primary, 70 | R.color.primary_dark, R.color.primary_light, 71 | R.color.accent); 72 | mSwipeRefreshWidget.setOnRefreshListener(this); 73 | 74 | mRecyclerView = (RecyclerView) view.findViewById(R.id.recycle_view); 75 | mRecyclerView.setHasFixedSize(true); 76 | 77 | mLayoutManager = new LinearLayoutManager(getActivity()); 78 | mRecyclerView.setLayoutManager(mLayoutManager); 79 | 80 | mRecyclerView.setItemAnimator(new DefaultItemAnimator()); 81 | mAdapter = new NbaAdapter(getActivity().getApplicationContext()); 82 | mAdapter.setOnItemClickListener(mOnItemClickListener); 83 | mRecyclerView.setAdapter(mAdapter); 84 | mRecyclerView.setOnScrollListener(mOnScrollListener); 85 | onRefresh(); 86 | return view; 87 | } 88 | 89 | private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() { 90 | 91 | private int lastVisibleItem; 92 | 93 | @Override 94 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 95 | super.onScrolled(recyclerView, dx, dy); 96 | lastVisibleItem = mLayoutManager.findLastVisibleItemPosition(); 97 | } 98 | 99 | @Override 100 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 101 | super.onScrollStateChanged(recyclerView, newState); 102 | if (newState == RecyclerView.SCROLL_STATE_IDLE 103 | && lastVisibleItem + 1 == mAdapter.getItemCount() 104 | && mAdapter.isShowFooter()) { 105 | //加载更多 106 | LogUtils.d(TAG, "loading more data"); 107 | 108 | // mNewsPresenter.loadNews(mType,""); 109 | } 110 | } 111 | }; 112 | 113 | private NbaAdapter.OnItemClickListener mOnItemClickListener = new NbaAdapter.OnItemClickListener() { 114 | @Override 115 | public void onItemClick(View view, int position) { 116 | NbaBean news = mAdapter.getItem(position); 117 | Intent intent = new Intent(getActivity(), NbaDetailActivity.class); 118 | intent.putExtra("news", news); 119 | 120 | View transitionView = view.findViewById(R.id.ivNews); 121 | ActivityOptionsCompat options = 122 | ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), 123 | transitionView, getString(R.string.transition_news_img)); 124 | 125 | ActivityCompat.startActivity(getActivity(), intent, options.toBundle()); 126 | } 127 | }; 128 | 129 | @Override 130 | public void showProgress() { 131 | mSwipeRefreshWidget.setRefreshing(true); 132 | } 133 | 134 | @Override 135 | public void addNews(List newsList) { 136 | mAdapter.isShowFooter(true); 137 | if (mData == null) { 138 | mData = new ArrayList(); 139 | } 140 | mData.addAll(newsList); 141 | 142 | mAdapter.setmDate(mData); 143 | 144 | mAdapter.notifyDataSetChanged(); 145 | 146 | 147 | } 148 | 149 | @Override 150 | public void hideProgress() { 151 | mSwipeRefreshWidget.setRefreshing(false); 152 | } 153 | 154 | @Override 155 | public void showLoadFailMsg() { 156 | if (pageIndex == 0) { 157 | mAdapter.isShowFooter(false); 158 | mAdapter.notifyDataSetChanged(); 159 | } 160 | } 161 | 162 | @Override 163 | public void onRefresh() { 164 | pageIndex = 0; 165 | if (mData != null) { 166 | mData.clear(); 167 | } 168 | System.out.println("mType====="+mType); 169 | mNewsPresenter.loadNews(mType,""); 170 | // mNewsPresenter.loadNews("http://nbaplus.sinaapp.com/api/v1.0/news/update",this); 171 | 172 | } 173 | 174 | } 175 | 176 | -------------------------------------------------------------------------------- /swipeback/src/main/java/me/imid/swipebacklayout/lib/SwipeBackLayout.java: -------------------------------------------------------------------------------- 1 | package me.imid.swipebacklayout.lib; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Canvas; 7 | import android.graphics.Rect; 8 | import android.graphics.drawable.Drawable; 9 | import android.support.v4.view.ViewCompat; 10 | import android.util.AttributeSet; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.FrameLayout; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | public class SwipeBackLayout extends FrameLayout { 20 | /** 21 | * Minimum velocity that will be detected as a fling 22 | */ 23 | private static final int MIN_FLING_VELOCITY = 400; // dips per second 24 | 25 | private static final int DEFAULT_SCRIM_COLOR = 0x99000000; 26 | 27 | private static final int FULL_ALPHA = 255; 28 | 29 | /** 30 | * Edge flag indicating that the left edge should be affected. 31 | */ 32 | public static final int EDGE_LEFT = ViewDragHelper.EDGE_LEFT; 33 | 34 | /** 35 | * Edge flag indicating that the right edge should be affected. 36 | */ 37 | public static final int EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT; 38 | 39 | /** 40 | * Edge flag indicating that the bottom edge should be affected. 41 | */ 42 | public static final int EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM; 43 | 44 | /** 45 | * Edge flag set indicating all edges should be affected. 46 | */ 47 | public static final int EDGE_ALL = EDGE_LEFT | EDGE_RIGHT | EDGE_BOTTOM; 48 | 49 | /** 50 | * A view is not currently being dragged or animating as a result of a 51 | * fling/snap. 52 | */ 53 | public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE; 54 | 55 | /** 56 | * A view is currently being dragged. The position is currently changing as 57 | * a result of user input or simulated user input. 58 | */ 59 | public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING; 60 | 61 | /** 62 | * A view is currently settling into place as a result of a fling or 63 | * predefined non-interactive motion. 64 | */ 65 | public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING; 66 | 67 | /** 68 | * Default threshold of scroll 69 | */ 70 | private static final float DEFAULT_SCROLL_THRESHOLD = 0.3f; 71 | 72 | private static final int OVERSCROLL_DISTANCE = 10; 73 | 74 | private static final int[] EDGE_FLAGS = { 75 | EDGE_LEFT, EDGE_RIGHT, EDGE_BOTTOM, EDGE_ALL 76 | }; 77 | 78 | private int mEdgeFlag; 79 | 80 | /** 81 | * Threshold of scroll, we will close the activity, when scrollPercent over 82 | * this value; 83 | */ 84 | private float mScrollThreshold = DEFAULT_SCROLL_THRESHOLD; 85 | 86 | private Activity mActivity; 87 | 88 | private boolean mEnable = true; 89 | 90 | private View mContentView; 91 | 92 | private ViewDragHelper mDragHelper; 93 | 94 | private float mScrollPercent; 95 | 96 | private int mContentLeft; 97 | 98 | private int mContentTop; 99 | 100 | /** 101 | * The set of listeners to be sent events through. 102 | */ 103 | private List mListeners; 104 | 105 | private Drawable mShadowLeft; 106 | 107 | private Drawable mShadowRight; 108 | 109 | private Drawable mShadowBottom; 110 | 111 | private float mScrimOpacity; 112 | 113 | private int mScrimColor = DEFAULT_SCRIM_COLOR; 114 | 115 | private boolean mInLayout; 116 | 117 | private Rect mTmpRect = new Rect(); 118 | 119 | /** 120 | * Edge being dragged 121 | */ 122 | private int mTrackingEdge; 123 | 124 | public SwipeBackLayout(Context context) { 125 | this(context, null); 126 | } 127 | 128 | public SwipeBackLayout(Context context, AttributeSet attrs) { 129 | this(context, attrs, R.attr.SwipeBackLayoutStyle); 130 | } 131 | 132 | public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) { 133 | super(context, attrs); 134 | mDragHelper = ViewDragHelper.create(this, new ViewDragCallback()); 135 | 136 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeBackLayout, defStyle, 137 | R.style.SwipeBackLayout); 138 | 139 | int edgeSize = a.getDimensionPixelSize(R.styleable.SwipeBackLayout_edge_size, -1); 140 | if (edgeSize > 0) 141 | setEdgeSize(edgeSize); 142 | int mode = EDGE_FLAGS[a.getInt(R.styleable.SwipeBackLayout_edge_flag, 0)]; 143 | setEdgeTrackingEnabled(mode); 144 | 145 | int shadowLeft = a.getResourceId(R.styleable.SwipeBackLayout_shadow_left, 146 | R.drawable.shadow_left); 147 | int shadowRight = a.getResourceId(R.styleable.SwipeBackLayout_shadow_right, 148 | R.drawable.shadow_right); 149 | int shadowBottom = a.getResourceId(R.styleable.SwipeBackLayout_shadow_bottom, 150 | R.drawable.shadow_bottom); 151 | setShadow(shadowLeft, EDGE_LEFT); 152 | setShadow(shadowRight, EDGE_RIGHT); 153 | setShadow(shadowBottom, EDGE_BOTTOM); 154 | a.recycle(); 155 | final float density = getResources().getDisplayMetrics().density; 156 | final float minVel = MIN_FLING_VELOCITY * density; 157 | mDragHelper.setMinVelocity(minVel); 158 | mDragHelper.setMaxVelocity(minVel * 2f); 159 | } 160 | 161 | /** 162 | * Sets the sensitivity of the NavigationLayout. 163 | * 164 | * @param context The application context. 165 | * @param sensitivity value between 0 and 1, the final value for touchSlop = 166 | * ViewConfiguration.getScaledTouchSlop * (1 / s); 167 | */ 168 | public void setSensitivity(Context context, float sensitivity) { 169 | mDragHelper.setSensitivity(context, sensitivity); 170 | } 171 | 172 | /** 173 | * Set up contentView which will be moved by user gesture 174 | * 175 | * @param view 176 | */ 177 | private void setContentView(View view) { 178 | mContentView = view; 179 | } 180 | 181 | public void setEnableGesture(boolean enable) { 182 | mEnable = enable; 183 | } 184 | 185 | /** 186 | * Enable edge tracking for the selected edges of the parent view. The 187 | * callback's 188 | * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeTouched(int, int)} 189 | * and 190 | * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeDragStarted(int, int)} 191 | * methods will only be invoked for edges for which edge tracking has been 192 | * enabled. 193 | * 194 | * @param edgeFlags Combination of edge flags describing the edges to watch 195 | * @see #EDGE_LEFT 196 | * @see #EDGE_RIGHT 197 | * @see #EDGE_BOTTOM 198 | */ 199 | public void setEdgeTrackingEnabled(int edgeFlags) { 200 | mEdgeFlag = edgeFlags; 201 | mDragHelper.setEdgeTrackingEnabled(mEdgeFlag); 202 | } 203 | 204 | /** 205 | * Set a color to use for the scrim that obscures primary content while a 206 | * drawer is open. 207 | * 208 | * @param color Color to use in 0xAARRGGBB format. 209 | */ 210 | public void setScrimColor(int color) { 211 | mScrimColor = color; 212 | invalidate(); 213 | } 214 | 215 | /** 216 | * Set the size of an edge. This is the range in pixels along the edges of 217 | * this view that will actively detect edge touches or drags if edge 218 | * tracking is enabled. 219 | * 220 | * @param size The size of an edge in pixels 221 | */ 222 | public void setEdgeSize(int size) { 223 | mDragHelper.setEdgeSize(size); 224 | } 225 | 226 | /** 227 | * Register a callback to be invoked when a swipe event is sent to this 228 | * view. 229 | * 230 | * @param listener the swipe listener to attach to this view 231 | * @deprecated use {@link #addSwipeListener} instead 232 | */ 233 | @Deprecated 234 | public void setSwipeListener(SwipeListener listener) { 235 | addSwipeListener(listener); 236 | } 237 | 238 | /** 239 | * Add a callback to be invoked when a swipe event is sent to this view. 240 | * 241 | * @param listener the swipe listener to attach to this view 242 | */ 243 | public void addSwipeListener(SwipeListener listener) { 244 | if (mListeners == null) { 245 | mListeners = new ArrayList(); 246 | } 247 | mListeners.add(listener); 248 | } 249 | 250 | /** 251 | * Removes a listener from the set of listeners 252 | * 253 | * @param listener 254 | */ 255 | public void removeSwipeListener(SwipeListener listener) { 256 | if (mListeners == null) { 257 | return; 258 | } 259 | mListeners.remove(listener); 260 | } 261 | 262 | public static interface SwipeListener { 263 | /** 264 | * Invoke when state change 265 | * 266 | * @param state flag to describe scroll state 267 | * @param scrollPercent scroll percent of this view 268 | * @see #STATE_IDLE 269 | * @see #STATE_DRAGGING 270 | * @see #STATE_SETTLING 271 | */ 272 | public void onScrollStateChange(int state, float scrollPercent); 273 | 274 | /** 275 | * Invoke when edge touched 276 | * 277 | * @param edgeFlag edge flag describing the edge being touched 278 | * @see #EDGE_LEFT 279 | * @see #EDGE_RIGHT 280 | * @see #EDGE_BOTTOM 281 | */ 282 | public void onEdgeTouch(int edgeFlag); 283 | 284 | /** 285 | * Invoke when scroll percent over the threshold for the first time 286 | */ 287 | public void onScrollOverThreshold(); 288 | } 289 | 290 | /** 291 | * Set scroll threshold, we will close the activity, when scrollPercent over 292 | * this value 293 | * 294 | * @param threshold 295 | */ 296 | public void setScrollThresHold(float threshold) { 297 | if (threshold >= 1.0f || threshold <= 0) { 298 | throw new IllegalArgumentException("Threshold value should be between 0 and 1.0"); 299 | } 300 | mScrollThreshold = threshold; 301 | } 302 | 303 | /** 304 | * Set a drawable used for edge shadow. 305 | * 306 | * @param shadow Drawable to use 307 | * @param edgeFlags Combination of edge flags describing the edge to set 308 | * @see #EDGE_LEFT 309 | * @see #EDGE_RIGHT 310 | * @see #EDGE_BOTTOM 311 | */ 312 | public void setShadow(Drawable shadow, int edgeFlag) { 313 | if ((edgeFlag & EDGE_LEFT) != 0) { 314 | mShadowLeft = shadow; 315 | } else if ((edgeFlag & EDGE_RIGHT) != 0) { 316 | mShadowRight = shadow; 317 | } else if ((edgeFlag & EDGE_BOTTOM) != 0) { 318 | mShadowBottom = shadow; 319 | } 320 | invalidate(); 321 | } 322 | 323 | /** 324 | * Set a drawable used for edge shadow. 325 | * 326 | * @param resId Resource of drawable to use 327 | * @param edgeFlags Combination of edge flags describing the edge to set 328 | * @see #EDGE_LEFT 329 | * @see #EDGE_RIGHT 330 | * @see #EDGE_BOTTOM 331 | */ 332 | public void setShadow(int resId, int edgeFlag) { 333 | setShadow(getResources().getDrawable(resId), edgeFlag); 334 | } 335 | 336 | /** 337 | * Scroll out contentView and finish the activity 338 | */ 339 | public void scrollToFinishActivity() { 340 | final int childWidth = mContentView.getWidth(); 341 | final int childHeight = mContentView.getHeight(); 342 | 343 | int left = 0, top = 0; 344 | if ((mEdgeFlag & EDGE_LEFT) != 0) { 345 | left = childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE; 346 | mTrackingEdge = EDGE_LEFT; 347 | } else if ((mEdgeFlag & EDGE_RIGHT) != 0) { 348 | left = -childWidth - mShadowRight.getIntrinsicWidth() - OVERSCROLL_DISTANCE; 349 | mTrackingEdge = EDGE_RIGHT; 350 | } else if ((mEdgeFlag & EDGE_BOTTOM) != 0) { 351 | top = -childHeight - mShadowBottom.getIntrinsicHeight() - OVERSCROLL_DISTANCE; 352 | mTrackingEdge = EDGE_BOTTOM; 353 | } 354 | 355 | mDragHelper.smoothSlideViewTo(mContentView, left, top); 356 | invalidate(); 357 | } 358 | 359 | @Override 360 | public boolean onInterceptTouchEvent(MotionEvent event) { 361 | if (!mEnable) { 362 | return false; 363 | } 364 | try { 365 | return mDragHelper.shouldInterceptTouchEvent(event); 366 | } catch (ArrayIndexOutOfBoundsException e) { 367 | // FIXME: handle exception 368 | // issues #9 369 | return false; 370 | } 371 | } 372 | 373 | @Override 374 | public boolean onTouchEvent(MotionEvent event) { 375 | if (!mEnable) { 376 | return false; 377 | } 378 | mDragHelper.processTouchEvent(event); 379 | return true; 380 | } 381 | 382 | @Override 383 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 384 | mInLayout = true; 385 | if (mContentView != null) 386 | mContentView.layout(mContentLeft, mContentTop, 387 | mContentLeft + mContentView.getMeasuredWidth(), 388 | mContentTop + mContentView.getMeasuredHeight()); 389 | mInLayout = false; 390 | } 391 | 392 | @Override 393 | public void requestLayout() { 394 | if (!mInLayout) { 395 | super.requestLayout(); 396 | } 397 | } 398 | 399 | @Override 400 | protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 401 | final boolean drawContent = child == mContentView; 402 | 403 | boolean ret = super.drawChild(canvas, child, drawingTime); 404 | if (mScrimOpacity > 0 && drawContent 405 | && mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) { 406 | drawShadow(canvas, child); 407 | drawScrim(canvas, child); 408 | } 409 | return ret; 410 | } 411 | 412 | private void drawScrim(Canvas canvas, View child) { 413 | final int baseAlpha = (mScrimColor & 0xff000000) >>> 24; 414 | final int alpha = (int) (baseAlpha * mScrimOpacity); 415 | final int color = alpha << 24 | (mScrimColor & 0xffffff); 416 | 417 | if ((mTrackingEdge & EDGE_LEFT) != 0) { 418 | canvas.clipRect(0, 0, child.getLeft(), getHeight()); 419 | } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { 420 | canvas.clipRect(child.getRight(), 0, getRight(), getHeight()); 421 | } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) { 422 | canvas.clipRect(child.getLeft(), child.getBottom(), getRight(), getHeight()); 423 | } 424 | canvas.drawColor(color); 425 | } 426 | 427 | private void drawShadow(Canvas canvas, View child) { 428 | final Rect childRect = mTmpRect; 429 | child.getHitRect(childRect); 430 | 431 | if ((mEdgeFlag & EDGE_LEFT) != 0) { 432 | mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top, 433 | childRect.left, childRect.bottom); 434 | mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); 435 | mShadowLeft.draw(canvas); 436 | } 437 | 438 | if ((mEdgeFlag & EDGE_RIGHT) != 0) { 439 | mShadowRight.setBounds(childRect.right, childRect.top, 440 | childRect.right + mShadowRight.getIntrinsicWidth(), childRect.bottom); 441 | mShadowRight.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); 442 | mShadowRight.draw(canvas); 443 | } 444 | 445 | if ((mEdgeFlag & EDGE_BOTTOM) != 0) { 446 | mShadowBottom.setBounds(childRect.left, childRect.bottom, childRect.right, 447 | childRect.bottom + mShadowBottom.getIntrinsicHeight()); 448 | mShadowBottom.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); 449 | mShadowBottom.draw(canvas); 450 | } 451 | } 452 | 453 | public void attachToActivity(Activity activity) { 454 | mActivity = activity; 455 | TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{ 456 | android.R.attr.windowBackground 457 | }); 458 | int background = a.getResourceId(0, 0); 459 | a.recycle(); 460 | 461 | ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView(); 462 | ViewGroup decorChild = (ViewGroup) decor.getChildAt(0); 463 | decorChild.setBackgroundResource(background); 464 | decor.removeView(decorChild); 465 | addView(decorChild); 466 | setContentView(decorChild); 467 | decor.addView(this); 468 | } 469 | 470 | @Override 471 | public void computeScroll() { 472 | mScrimOpacity = 1 - mScrollPercent; 473 | if (mDragHelper.continueSettling(true)) { 474 | ViewCompat.postInvalidateOnAnimation(this); 475 | } 476 | } 477 | 478 | private class ViewDragCallback extends ViewDragHelper.Callback { 479 | private boolean mIsScrollOverValid; 480 | 481 | @Override 482 | public boolean tryCaptureView(View view, int i) { 483 | boolean ret = mDragHelper.isEdgeTouched(mEdgeFlag, i); 484 | if (ret) { 485 | if (mDragHelper.isEdgeTouched(EDGE_LEFT, i)) { 486 | mTrackingEdge = EDGE_LEFT; 487 | } else if (mDragHelper.isEdgeTouched(EDGE_RIGHT, i)) { 488 | mTrackingEdge = EDGE_RIGHT; 489 | } else if (mDragHelper.isEdgeTouched(EDGE_BOTTOM, i)) { 490 | mTrackingEdge = EDGE_BOTTOM; 491 | } 492 | if (mListeners != null && !mListeners.isEmpty()) { 493 | for (SwipeListener listener : mListeners) { 494 | listener.onEdgeTouch(mTrackingEdge); 495 | } 496 | } 497 | mIsScrollOverValid = true; 498 | } 499 | boolean directionCheck = false; 500 | if (mEdgeFlag == EDGE_LEFT || mEdgeFlag == EDGE_RIGHT) { 501 | directionCheck = !mDragHelper.checkTouchSlop(ViewDragHelper.DIRECTION_VERTICAL, i); 502 | } else if (mEdgeFlag == EDGE_BOTTOM) { 503 | directionCheck = !mDragHelper 504 | .checkTouchSlop(ViewDragHelper.DIRECTION_HORIZONTAL, i); 505 | } else if (mEdgeFlag == EDGE_ALL) { 506 | directionCheck = true; 507 | } 508 | return ret & directionCheck; 509 | } 510 | 511 | @Override 512 | public int getViewHorizontalDragRange(View child) { 513 | return mEdgeFlag & (EDGE_LEFT | EDGE_RIGHT); 514 | } 515 | 516 | @Override 517 | public int getViewVerticalDragRange(View child) { 518 | return mEdgeFlag & EDGE_BOTTOM; 519 | } 520 | 521 | @Override 522 | public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 523 | super.onViewPositionChanged(changedView, left, top, dx, dy); 524 | if ((mTrackingEdge & EDGE_LEFT) != 0) { 525 | mScrollPercent = Math.abs((float) left 526 | / (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth())); 527 | } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { 528 | mScrollPercent = Math.abs((float) left 529 | / (mContentView.getWidth() + mShadowRight.getIntrinsicWidth())); 530 | } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) { 531 | mScrollPercent = Math.abs((float) top 532 | / (mContentView.getHeight() + mShadowBottom.getIntrinsicHeight())); 533 | } 534 | mContentLeft = left; 535 | mContentTop = top; 536 | invalidate(); 537 | if (mScrollPercent < mScrollThreshold && !mIsScrollOverValid) { 538 | mIsScrollOverValid = true; 539 | } 540 | if (mListeners != null && !mListeners.isEmpty() 541 | && mDragHelper.getViewDragState() == STATE_DRAGGING 542 | && mScrollPercent >= mScrollThreshold && mIsScrollOverValid) { 543 | mIsScrollOverValid = false; 544 | for (SwipeListener listener : mListeners) { 545 | listener.onScrollOverThreshold(); 546 | } 547 | } 548 | 549 | if (mScrollPercent >= 1) { 550 | if (!mActivity.isFinishing()) { 551 | mActivity.finish(); 552 | mActivity.overridePendingTransition(0, 0); 553 | } 554 | } 555 | } 556 | 557 | @Override 558 | public void onViewReleased(View releasedChild, float xvel, float yvel) { 559 | final int childWidth = releasedChild.getWidth(); 560 | final int childHeight = releasedChild.getHeight(); 561 | 562 | int left = 0, top = 0; 563 | if ((mTrackingEdge & EDGE_LEFT) != 0) { 564 | left = xvel > 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? childWidth 565 | + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE : 0; 566 | } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { 567 | left = xvel < 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? -(childWidth 568 | + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0; 569 | } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) { 570 | top = yvel < 0 || yvel == 0 && mScrollPercent > mScrollThreshold ? -(childHeight 571 | + mShadowBottom.getIntrinsicHeight() + OVERSCROLL_DISTANCE) : 0; 572 | } 573 | 574 | mDragHelper.settleCapturedViewAt(left, top); 575 | invalidate(); 576 | } 577 | 578 | @Override 579 | public int clampViewPositionHorizontal(View child, int left, int dx) { 580 | int ret = 0; 581 | if ((mTrackingEdge & EDGE_LEFT) != 0) { 582 | ret = Math.min(child.getWidth(), Math.max(left, 0)); 583 | } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { 584 | ret = Math.min(0, Math.max(left, -child.getWidth())); 585 | } 586 | return ret; 587 | } 588 | 589 | @Override 590 | public int clampViewPositionVertical(View child, int top, int dy) { 591 | int ret = 0; 592 | if ((mTrackingEdge & EDGE_BOTTOM) != 0) { 593 | ret = Math.min(0, Math.max(top, -child.getHeight())); 594 | } 595 | return ret; 596 | } 597 | 598 | @Override 599 | public void onViewDragStateChanged(int state) { 600 | super.onViewDragStateChanged(state); 601 | if (mListeners != null && !mListeners.isEmpty()) { 602 | for (SwipeListener listener : mListeners) { 603 | listener.onScrollStateChange(state, mScrollPercent); 604 | } 605 | } 606 | } 607 | } 608 | } 609 | -------------------------------------------------------------------------------- /swipeback/src/main/java/me/imid/swipebacklayout/lib/ViewDragHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 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 me.imid.swipebacklayout.lib; 18 | 19 | import android.content.Context; 20 | import android.support.v4.view.MotionEventCompat; 21 | import android.support.v4.view.VelocityTrackerCompat; 22 | import android.support.v4.view.ViewCompat; 23 | import android.support.v4.widget.ScrollerCompat; 24 | import android.view.MotionEvent; 25 | import android.view.VelocityTracker; 26 | import android.view.View; 27 | import android.view.ViewConfiguration; 28 | import android.view.ViewGroup; 29 | import android.view.animation.Interpolator; 30 | 31 | import java.util.Arrays; 32 | 33 | /** 34 | * ViewDragHelper is a utility class for writing custom ViewGroups. It offers a 35 | * number of useful operations and state tracking for allowing a user to drag 36 | * and reposition views within their parent ViewGroup. 37 | */ 38 | public class ViewDragHelper { 39 | private static final String TAG = "ViewDragHelper"; 40 | 41 | /** 42 | * A null/invalid pointer ID. 43 | */ 44 | public static final int INVALID_POINTER = -1; 45 | 46 | /** 47 | * A view is not currently being dragged or animating as a result of a 48 | * fling/snap. 49 | */ 50 | public static final int STATE_IDLE = 0; 51 | 52 | /** 53 | * A view is currently being dragged. The position is currently changing as 54 | * a result of user input or simulated user input. 55 | */ 56 | public static final int STATE_DRAGGING = 1; 57 | 58 | /** 59 | * A view is currently settling into place as a result of a fling or 60 | * predefined non-interactive motion. 61 | */ 62 | public static final int STATE_SETTLING = 2; 63 | 64 | /** 65 | * Edge flag indicating that the left edge should be affected. 66 | */ 67 | public static final int EDGE_LEFT = 1 << 0; 68 | 69 | /** 70 | * Edge flag indicating that the right edge should be affected. 71 | */ 72 | public static final int EDGE_RIGHT = 1 << 1; 73 | 74 | /** 75 | * Edge flag indicating that the top edge should be affected. 76 | */ 77 | public static final int EDGE_TOP = 1 << 2; 78 | 79 | /** 80 | * Edge flag indicating that the bottom edge should be affected. 81 | */ 82 | public static final int EDGE_BOTTOM = 1 << 3; 83 | 84 | /** 85 | * Edge flag set indicating all edges should be affected. 86 | */ 87 | public static final int EDGE_ALL = EDGE_LEFT | EDGE_TOP | EDGE_RIGHT | EDGE_BOTTOM; 88 | 89 | /** 90 | * Indicates that a check should occur along the horizontal axis 91 | */ 92 | public static final int DIRECTION_HORIZONTAL = 1 << 0; 93 | 94 | /** 95 | * Indicates that a check should occur along the vertical axis 96 | */ 97 | public static final int DIRECTION_VERTICAL = 1 << 1; 98 | 99 | /** 100 | * Indicates that a check should occur along all axes 101 | */ 102 | public static final int DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; 103 | 104 | public static final int EDGE_SIZE = 20; // dp 105 | 106 | private static final int BASE_SETTLE_DURATION = 256; // ms 107 | 108 | private static final int MAX_SETTLE_DURATION = 600; // ms 109 | 110 | // Current drag state; idle, dragging or settling 111 | private int mDragState; 112 | 113 | // Distance to travel before a drag may begin 114 | private int mTouchSlop; 115 | 116 | // Last known position/pointer tracking 117 | private int mActivePointerId = INVALID_POINTER; 118 | 119 | private float[] mInitialMotionX; 120 | 121 | private float[] mInitialMotionY; 122 | 123 | private float[] mLastMotionX; 124 | 125 | private float[] mLastMotionY; 126 | 127 | private int[] mInitialEdgeTouched; 128 | 129 | private int[] mEdgeDragsInProgress; 130 | 131 | private int[] mEdgeDragsLocked; 132 | 133 | private int mPointersDown; 134 | 135 | private VelocityTracker mVelocityTracker; 136 | 137 | private float mMaxVelocity; 138 | 139 | private float mMinVelocity; 140 | 141 | private int mEdgeSize; 142 | 143 | private int mTrackingEdges; 144 | 145 | private ScrollerCompat mScroller; 146 | 147 | private final Callback mCallback; 148 | 149 | private View mCapturedView; 150 | 151 | private boolean mReleaseInProgress; 152 | 153 | private final ViewGroup mParentView; 154 | 155 | /** 156 | * A Callback is used as a communication channel with the ViewDragHelper 157 | * back to the parent view using it. on*methods are invoked on 158 | * siginficant events and several accessor methods are expected to provide 159 | * the ViewDragHelper with more information about the state of the parent 160 | * view upon request. The callback also makes decisions governing the range 161 | * and draggability of child views. 162 | */ 163 | public static abstract class Callback { 164 | /** 165 | * Called when the drag state changes. See the STATE_* 166 | * constants for more information. 167 | * 168 | * @param state The new drag state 169 | * @see #STATE_IDLE 170 | * @see #STATE_DRAGGING 171 | * @see #STATE_SETTLING 172 | */ 173 | public void onViewDragStateChanged(int state) { 174 | } 175 | 176 | /** 177 | * Called when the captured view's position changes as the result of a 178 | * drag or settle. 179 | * 180 | * @param changedView View whose position changed 181 | * @param left New X coordinate of the left edge of the view 182 | * @param top New Y coordinate of the top edge of the view 183 | * @param dx Change in X position from the last call 184 | * @param dy Change in Y position from the last call 185 | */ 186 | public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 187 | } 188 | 189 | /** 190 | * Called when a child view is captured for dragging or settling. The ID 191 | * of the pointer currently dragging the captured view is supplied. If 192 | * activePointerId is identified as {@link #INVALID_POINTER} the capture 193 | * is programmatic instead of pointer-initiated. 194 | * 195 | * @param capturedChild Child view that was captured 196 | * @param activePointerId Pointer id tracking the child capture 197 | */ 198 | public void onViewCaptured(View capturedChild, int activePointerId) { 199 | } 200 | 201 | /** 202 | * Called when the child view is no longer being actively dragged. The 203 | * fling velocity is also supplied, if relevant. The velocity values may 204 | * be clamped to system minimums or maximums. 205 | *

206 | * Calling code may decide to fling or otherwise release the view to let 207 | * it settle into place. It should do so using 208 | * {@link #settleCapturedViewAt(int, int)} or 209 | * {@link #flingCapturedView(int, int, int, int)}. If the Callback 210 | * invokes one of these methods, the ViewDragHelper will enter 211 | * {@link #STATE_SETTLING} and the view capture will not fully end until 212 | * it comes to a complete stop. If neither of these methods is invoked 213 | * before onViewReleased returns, the view will stop in 214 | * place and the ViewDragHelper will return to {@link #STATE_IDLE}. 215 | *

216 | * 217 | * @param releasedChild The captured child view now being released 218 | * @param xvel X velocity of the pointer as it left the screen in pixels 219 | * per second. 220 | * @param yvel Y velocity of the pointer as it left the screen in pixels 221 | * per second. 222 | */ 223 | public void onViewReleased(View releasedChild, float xvel, float yvel) { 224 | } 225 | 226 | /** 227 | * Called when one of the subscribed edges in the parent view has been 228 | * touched by the user while no child view is currently captured. 229 | * 230 | * @param edgeFlags A combination of edge flags describing the edge(s) 231 | * currently touched 232 | * @param pointerId ID of the pointer touching the described edge(s) 233 | * @see #EDGE_LEFT 234 | * @see #EDGE_TOP 235 | * @see #EDGE_RIGHT 236 | * @see #EDGE_BOTTOM 237 | */ 238 | public void onEdgeTouched(int edgeFlags, int pointerId) { 239 | } 240 | 241 | /** 242 | * Called when the given edge may become locked. This can happen if an 243 | * edge drag was preliminarily rejected before beginning, but after 244 | * {@link #onEdgeTouched(int, int)} was called. This method should 245 | * return true to lock this edge or false to leave it unlocked. The 246 | * default behavior is to leave edges unlocked. 247 | * 248 | * @param edgeFlags A combination of edge flags describing the edge(s) 249 | * locked 250 | * @return true to lock the edge, false to leave it unlocked 251 | */ 252 | public boolean onEdgeLock(int edgeFlags) { 253 | return false; 254 | } 255 | 256 | /** 257 | * Called when the user has started a deliberate drag away from one of 258 | * the subscribed edges in the parent view while no child view is 259 | * currently captured. 260 | * 261 | * @param edgeFlags A combination of edge flags describing the edge(s) 262 | * dragged 263 | * @param pointerId ID of the pointer touching the described edge(s) 264 | * @see #EDGE_LEFT 265 | * @see #EDGE_TOP 266 | * @see #EDGE_RIGHT 267 | * @see #EDGE_BOTTOM 268 | */ 269 | public void onEdgeDragStarted(int edgeFlags, int pointerId) { 270 | } 271 | 272 | /** 273 | * Called to determine the Z-order of child views. 274 | * 275 | * @param index the ordered position to query for 276 | * @return index of the view that should be ordered at position 277 | * index 278 | */ 279 | public int getOrderedChildIndex(int index) { 280 | return index; 281 | } 282 | 283 | /** 284 | * Return the magnitude of a draggable child view's horizontal range of 285 | * motion in pixels. This method should return 0 for views that cannot 286 | * move horizontally. 287 | * 288 | * @param child Child view to check 289 | * @return range of horizontal motion in pixels 290 | */ 291 | public int getViewHorizontalDragRange(View child) { 292 | return 0; 293 | } 294 | 295 | /** 296 | * Return the magnitude of a draggable child view's vertical range of 297 | * motion in pixels. This method should return 0 for views that cannot 298 | * move vertically. 299 | * 300 | * @param child Child view to check 301 | * @return range of vertical motion in pixels 302 | */ 303 | public int getViewVerticalDragRange(View child) { 304 | return 0; 305 | } 306 | 307 | /** 308 | * Called when the user's input indicates that they want to capture the 309 | * given child view with the pointer indicated by pointerId. The 310 | * callback should return true if the user is permitted to drag the 311 | * given view with the indicated pointer. 312 | *

313 | * ViewDragHelper may call this method multiple times for the same view 314 | * even if the view is already captured; this indicates that a new 315 | * pointer is trying to take control of the view. 316 | *

317 | *

318 | * If this method returns true, a call to 319 | * {@link #onViewCaptured(android.view.View, int)} will follow if the 320 | * capture is successful. 321 | *

322 | * 323 | * @param child Child the user is attempting to capture 324 | * @param pointerId ID of the pointer attempting the capture 325 | * @return true if capture should be allowed, false otherwise 326 | */ 327 | public abstract boolean tryCaptureView(View child, int pointerId); 328 | 329 | /** 330 | * Restrict the motion of the dragged child view along the horizontal 331 | * axis. The default implementation does not allow horizontal motion; 332 | * the extending class must override this method and provide the desired 333 | * clamping. 334 | * 335 | * @param child Child view being dragged 336 | * @param left Attempted motion along the X axis 337 | * @param dx Proposed change in position for left 338 | * @return The new clamped position for left 339 | */ 340 | public int clampViewPositionHorizontal(View child, int left, int dx) { 341 | return 0; 342 | } 343 | 344 | /** 345 | * Restrict the motion of the dragged child view along the vertical 346 | * axis. The default implementation does not allow vertical motion; the 347 | * extending class must override this method and provide the desired 348 | * clamping. 349 | * 350 | * @param child Child view being dragged 351 | * @param top Attempted motion along the Y axis 352 | * @param dy Proposed change in position for top 353 | * @return The new clamped position for top 354 | */ 355 | public int clampViewPositionVertical(View child, int top, int dy) { 356 | return 0; 357 | } 358 | } 359 | 360 | /** 361 | * Interpolator defining the animation curve for mScroller 362 | */ 363 | private static final Interpolator sInterpolator = new Interpolator() { 364 | public float getInterpolation(float t) { 365 | t -= 1.0f; 366 | return t * t * t * t * t + 1.0f; 367 | } 368 | }; 369 | 370 | private final Runnable mSetIdleRunnable = new Runnable() { 371 | public void run() { 372 | setDragState(STATE_IDLE); 373 | } 374 | }; 375 | 376 | /** 377 | * Factory method to create a new ViewDragHelper. 378 | * 379 | * @param forParent Parent view to monitor 380 | * @param cb Callback to provide information and receive events 381 | * @return a new ViewDragHelper instance 382 | */ 383 | public static ViewDragHelper create(ViewGroup forParent, Callback cb) { 384 | return new ViewDragHelper(forParent.getContext(), forParent, cb); 385 | } 386 | 387 | /** 388 | * Factory method to create a new ViewDragHelper. 389 | * 390 | * @param forParent Parent view to monitor 391 | * @param sensitivity Multiplier for how sensitive the helper should be 392 | * about detecting the start of a drag. Larger values are more 393 | * sensitive. 1.0f is normal. 394 | * @param cb Callback to provide information and receive events 395 | * @return a new ViewDragHelper instance 396 | */ 397 | public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) { 398 | final ViewDragHelper helper = create(forParent, cb); 399 | helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity)); 400 | return helper; 401 | } 402 | 403 | /** 404 | * Apps should use ViewDragHelper.create() to get a new instance. This will 405 | * allow VDH to use internal compatibility implementations for different 406 | * platform versions. 407 | * 408 | * @param context Context to initialize config-dependent params from 409 | * @param forParent Parent view to monitor 410 | */ 411 | private ViewDragHelper(Context context, ViewGroup forParent, Callback cb) { 412 | if (forParent == null) { 413 | throw new IllegalArgumentException("Parent view may not be null"); 414 | } 415 | if (cb == null) { 416 | throw new IllegalArgumentException("Callback may not be null"); 417 | } 418 | 419 | mParentView = forParent; 420 | mCallback = cb; 421 | 422 | final ViewConfiguration vc = ViewConfiguration.get(context); 423 | final float density = context.getResources().getDisplayMetrics().density; 424 | mEdgeSize = (int) (EDGE_SIZE * density + 0.5f); 425 | 426 | mTouchSlop = vc.getScaledTouchSlop(); 427 | mMaxVelocity = vc.getScaledMaximumFlingVelocity(); 428 | mMinVelocity = vc.getScaledMinimumFlingVelocity(); 429 | mScroller = ScrollerCompat.create(context, sInterpolator); 430 | } 431 | 432 | /** 433 | * Sets the sensitivity of the dragger. 434 | * 435 | * @param context The application context. 436 | * @param sensitivity value between 0 and 1, the final value for touchSlop = 437 | * ViewConfiguration.getScaledTouchSlop * (1 / s); 438 | */ 439 | public void setSensitivity(Context context, float sensitivity) { 440 | float s = Math.max(0f, Math.min(1.0f, sensitivity)); 441 | ViewConfiguration viewConfiguration = ViewConfiguration.get(context); 442 | mTouchSlop = (int) (viewConfiguration.getScaledTouchSlop() * (1 / s)); 443 | } 444 | 445 | /** 446 | * Set the minimum velocity that will be detected as having a magnitude 447 | * greater than zero in pixels per second. Callback methods accepting a 448 | * velocity will be clamped appropriately. 449 | * 450 | * @param minVel minimum velocity to detect 451 | */ 452 | public void setMinVelocity(float minVel) { 453 | mMinVelocity = minVel; 454 | } 455 | 456 | /** 457 | * Set the max velocity that will be detected as having a magnitude 458 | * greater than zero in pixels per second. Callback methods accepting a 459 | * velocity will be clamped appropriately. 460 | * 461 | * @param maxVel max velocity to detect 462 | */ 463 | public void setMaxVelocity(float maxVel) { 464 | mMaxVelocity = maxVel; 465 | } 466 | 467 | /** 468 | * Return the currently configured minimum velocity. Any flings with a 469 | * magnitude less than this value in pixels per second. Callback methods 470 | * accepting a velocity will receive zero as a velocity value if the real 471 | * detected velocity was below this threshold. 472 | * 473 | * @return the minimum velocity that will be detected 474 | */ 475 | public float getMinVelocity() { 476 | return mMinVelocity; 477 | } 478 | 479 | /** 480 | * Retrieve the current drag state of this helper. This will return one of 481 | * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}. 482 | * 483 | * @return The current drag state 484 | */ 485 | public int getViewDragState() { 486 | return mDragState; 487 | } 488 | 489 | /** 490 | * Enable edge tracking for the selected edges of the parent view. The 491 | * callback's 492 | * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeTouched(int, int)} 493 | * and 494 | * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeDragStarted(int, int)} 495 | * methods will only be invoked for edges for which edge tracking has been 496 | * enabled. 497 | * 498 | * @param edgeFlags Combination of edge flags describing the edges to watch 499 | * @see #EDGE_LEFT 500 | * @see #EDGE_TOP 501 | * @see #EDGE_RIGHT 502 | * @see #EDGE_BOTTOM 503 | */ 504 | public void setEdgeTrackingEnabled(int edgeFlags) { 505 | mTrackingEdges = edgeFlags; 506 | } 507 | 508 | /** 509 | * Return the size of an edge. This is the range in pixels along the edges 510 | * of this view that will actively detect edge touches or drags if edge 511 | * tracking is enabled. 512 | * 513 | * @return The size of an edge in pixels 514 | * @see #setEdgeTrackingEnabled(int) 515 | */ 516 | public int getEdgeSize() { 517 | return mEdgeSize; 518 | } 519 | 520 | /** 521 | * Set the size of an edge. This is the range in pixels along the edges of 522 | * this view that will actively detect edge touches or drags if edge 523 | * tracking is enabled. 524 | * 525 | * @param size The size of an edge in pixels 526 | */ 527 | public void setEdgeSize(int size) { 528 | mEdgeSize = size; 529 | } 530 | 531 | /** 532 | * Capture a specific child view for dragging within the parent. The 533 | * callback will be notified but 534 | * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#tryCaptureView(android.view.View, int)} 535 | * will not be asked permission to capture this view. 536 | * 537 | * @param childView Child view to capture 538 | * @param activePointerId ID of the pointer that is dragging the captured 539 | * child view 540 | */ 541 | public void captureChildView(View childView, int activePointerId) { 542 | if (childView.getParent() != mParentView) { 543 | throw new IllegalArgumentException("captureChildView: parameter must be a descendant " 544 | + "of the ViewDragHelper's tracked parent view (" + mParentView + ")"); 545 | } 546 | 547 | mCapturedView = childView; 548 | mActivePointerId = activePointerId; 549 | mCallback.onViewCaptured(childView, activePointerId); 550 | setDragState(STATE_DRAGGING); 551 | } 552 | 553 | /** 554 | * @return The currently captured view, or null if no view has been 555 | * captured. 556 | */ 557 | public View getCapturedView() { 558 | return mCapturedView; 559 | } 560 | 561 | /** 562 | * @return The ID of the pointer currently dragging the captured view, or 563 | * {@link #INVALID_POINTER}. 564 | */ 565 | public int getActivePointerId() { 566 | return mActivePointerId; 567 | } 568 | 569 | /** 570 | * @return The minimum distance in pixels that the user must travel to 571 | * initiate a drag 572 | */ 573 | public int getTouchSlop() { 574 | return mTouchSlop; 575 | } 576 | 577 | /** 578 | * The result of a call to this method is equivalent to 579 | * {@link #processTouchEvent(android.view.MotionEvent)} receiving an 580 | * ACTION_CANCEL event. 581 | */ 582 | public void cancel() { 583 | mActivePointerId = INVALID_POINTER; 584 | clearMotionHistory(); 585 | 586 | if (mVelocityTracker != null) { 587 | mVelocityTracker.recycle(); 588 | mVelocityTracker = null; 589 | } 590 | } 591 | 592 | /** 593 | * {@link #cancel()}, but also abort all motion in progress and snap to the 594 | * end of any animation. 595 | */ 596 | public void abort() { 597 | cancel(); 598 | if (mDragState == STATE_SETTLING) { 599 | final int oldX = mScroller.getCurrX(); 600 | final int oldY = mScroller.getCurrY(); 601 | mScroller.abortAnimation(); 602 | final int newX = mScroller.getCurrX(); 603 | final int newY = mScroller.getCurrY(); 604 | mCallback.onViewPositionChanged(mCapturedView, newX, newY, newX - oldX, newY - oldY); 605 | } 606 | setDragState(STATE_IDLE); 607 | } 608 | 609 | /** 610 | * Animate the view child to the given (left, top) position. If 611 | * this method returns true, the caller should invoke 612 | * {@link #continueSettling(boolean)} on each subsequent frame to continue 613 | * the motion until it returns false. If this method returns false there is 614 | * no further work to do to complete the movement. 615 | *

616 | * This operation does not count as a capture event, though 617 | * {@link #getCapturedView()} will still report the sliding view while the 618 | * slide is in progress. 619 | *

620 | * 621 | * @param child Child view to capture and animate 622 | * @param finalLeft Final left position of child 623 | * @param finalTop Final top position of child 624 | * @return true if animation should continue through 625 | * {@link #continueSettling(boolean)} calls 626 | */ 627 | public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) { 628 | mCapturedView = child; 629 | mActivePointerId = INVALID_POINTER; 630 | 631 | return forceSettleCapturedViewAt(finalLeft, finalTop, 0, 0); 632 | } 633 | 634 | /** 635 | * Settle the captured view at the given (left, top) position. The 636 | * appropriate velocity from prior motion will be taken into account. If 637 | * this method returns true, the caller should invoke 638 | * {@link #continueSettling(boolean)} on each subsequent frame to continue 639 | * the motion until it returns false. If this method returns false there is 640 | * no further work to do to complete the movement. 641 | * 642 | * @param finalLeft Settled left edge position for the captured view 643 | * @param finalTop Settled top edge position for the captured view 644 | * @return true if animation should continue through 645 | * {@link #continueSettling(boolean)} calls 646 | */ 647 | public boolean settleCapturedViewAt(int finalLeft, int finalTop) { 648 | if (!mReleaseInProgress) { 649 | throw new IllegalStateException("Cannot settleCapturedViewAt outside of a call to " 650 | + "Callback#onViewReleased"); 651 | } 652 | 653 | return forceSettleCapturedViewAt(finalLeft, finalTop, 654 | (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId), 655 | (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId)); 656 | } 657 | 658 | /** 659 | * Settle the captured view at the given (left, top) position. 660 | * 661 | * @param finalLeft Target left position for the captured view 662 | * @param finalTop Target top position for the captured view 663 | * @param xvel Horizontal velocity 664 | * @param yvel Vertical velocity 665 | * @return true if animation should continue through 666 | * {@link #continueSettling(boolean)} calls 667 | */ 668 | private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) { 669 | final int startLeft = mCapturedView.getLeft(); 670 | final int startTop = mCapturedView.getTop(); 671 | final int dx = finalLeft - startLeft; 672 | final int dy = finalTop - startTop; 673 | 674 | if (dx == 0 && dy == 0) { 675 | // Nothing to do. Send callbacks, be done. 676 | mScroller.abortAnimation(); 677 | setDragState(STATE_IDLE); 678 | return false; 679 | } 680 | 681 | final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel); 682 | mScroller.startScroll(startLeft, startTop, dx, dy, duration); 683 | 684 | setDragState(STATE_SETTLING); 685 | return true; 686 | } 687 | 688 | private int computeSettleDuration(View child, int dx, int dy, int xvel, int yvel) { 689 | xvel = clampMag(xvel, (int) mMinVelocity, (int) mMaxVelocity); 690 | yvel = clampMag(yvel, (int) mMinVelocity, (int) mMaxVelocity); 691 | final int absDx = Math.abs(dx); 692 | final int absDy = Math.abs(dy); 693 | final int absXVel = Math.abs(xvel); 694 | final int absYVel = Math.abs(yvel); 695 | final int addedVel = absXVel + absYVel; 696 | final int addedDistance = absDx + absDy; 697 | 698 | final float xweight = xvel != 0 ? (float) absXVel / addedVel : (float) absDx 699 | / addedDistance; 700 | final float yweight = yvel != 0 ? (float) absYVel / addedVel : (float) absDy 701 | / addedDistance; 702 | 703 | int xduration = computeAxisDuration(dx, xvel, mCallback.getViewHorizontalDragRange(child)); 704 | int yduration = computeAxisDuration(dy, yvel, mCallback.getViewVerticalDragRange(child)); 705 | 706 | return (int) (xduration * xweight + yduration * yweight); 707 | } 708 | 709 | private int computeAxisDuration(int delta, int velocity, int motionRange) { 710 | if (delta == 0) { 711 | return 0; 712 | } 713 | 714 | final int width = mParentView.getWidth(); 715 | final int halfWidth = width / 2; 716 | final float distanceRatio = Math.min(1f, (float) Math.abs(delta) / width); 717 | final float distance = halfWidth + halfWidth 718 | * distanceInfluenceForSnapDuration(distanceRatio); 719 | 720 | int duration; 721 | velocity = Math.abs(velocity); 722 | if (velocity > 0) { 723 | duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); 724 | } else { 725 | final float range = (float) Math.abs(delta) / motionRange; 726 | duration = (int) ((range + 1) * BASE_SETTLE_DURATION); 727 | } 728 | return Math.min(duration, MAX_SETTLE_DURATION); 729 | } 730 | 731 | /** 732 | * Clamp the magnitude of value for absMin and absMax. If the value is below 733 | * the minimum, it will be clamped to zero. If the value is above the 734 | * maximum, it will be clamped to the maximum. 735 | * 736 | * @param value Value to clamp 737 | * @param absMin Absolute value of the minimum significant value to return 738 | * @param absMax Absolute value of the maximum value to return 739 | * @return The clamped value with the same sign as value 740 | */ 741 | private int clampMag(int value, int absMin, int absMax) { 742 | final int absValue = Math.abs(value); 743 | if (absValue < absMin) 744 | return 0; 745 | if (absValue > absMax) 746 | return value > 0 ? absMax : -absMax; 747 | return value; 748 | } 749 | 750 | /** 751 | * Clamp the magnitude of value for absMin and absMax. If the value is below 752 | * the minimum, it will be clamped to zero. If the value is above the 753 | * maximum, it will be clamped to the maximum. 754 | * 755 | * @param value Value to clamp 756 | * @param absMin Absolute value of the minimum significant value to return 757 | * @param absMax Absolute value of the maximum value to return 758 | * @return The clamped value with the same sign as value 759 | */ 760 | private float clampMag(float value, float absMin, float absMax) { 761 | final float absValue = Math.abs(value); 762 | if (absValue < absMin) 763 | return 0; 764 | if (absValue > absMax) 765 | return value > 0 ? absMax : -absMax; 766 | return value; 767 | } 768 | 769 | private float distanceInfluenceForSnapDuration(float f) { 770 | f -= 0.5f; // center the values about 0. 771 | f *= 0.3f * Math.PI / 2.0f; 772 | return (float) Math.sin(f); 773 | } 774 | 775 | /** 776 | * Settle the captured view based on standard free-moving fling behavior. 777 | * The caller should invoke {@link #continueSettling(boolean)} on each 778 | * subsequent frame to continue the motion until it returns false. 779 | * 780 | * @param minLeft Minimum X position for the view's left edge 781 | * @param minTop Minimum Y position for the view's top edge 782 | * @param maxLeft Maximum X position for the view's left edge 783 | * @param maxTop Maximum Y position for the view's top edge 784 | */ 785 | public void flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop) { 786 | if (!mReleaseInProgress) { 787 | throw new IllegalStateException("Cannot flingCapturedView outside of a call to " 788 | + "Callback#onViewReleased"); 789 | } 790 | 791 | mScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(), 792 | (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId), 793 | (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId), 794 | minLeft, maxLeft, minTop, maxTop); 795 | 796 | setDragState(STATE_SETTLING); 797 | } 798 | 799 | /** 800 | * Move the captured settling view by the appropriate amount for the current 801 | * time. If continueSettling returns true, the caller should 802 | * call it again on the next frame to continue. 803 | * 804 | * @param deferCallbacks true if state callbacks should be deferred via 805 | * posted message. Set this to true if you are calling this 806 | * method from {@link android.view.View#computeScroll()} or 807 | * similar methods invoked as part of layout or drawing. 808 | * @return true if settle is still in progress 809 | */ 810 | public boolean continueSettling(boolean deferCallbacks) { 811 | if (mDragState == STATE_SETTLING) { 812 | boolean keepGoing = mScroller.computeScrollOffset(); 813 | final int x = mScroller.getCurrX(); 814 | final int y = mScroller.getCurrY(); 815 | final int dx = x - mCapturedView.getLeft(); 816 | final int dy = y - mCapturedView.getTop(); 817 | 818 | if (dx != 0) { 819 | mCapturedView.offsetLeftAndRight(dx); 820 | } 821 | if (dy != 0) { 822 | mCapturedView.offsetTopAndBottom(dy); 823 | } 824 | 825 | if (dx != 0 || dy != 0) { 826 | mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy); 827 | } 828 | 829 | if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) { 830 | // Close enough. The interpolator/scroller might think we're 831 | // still moving 832 | // but the user sure doesn't. 833 | mScroller.abortAnimation(); 834 | keepGoing = mScroller.isFinished(); 835 | } 836 | 837 | if (!keepGoing) { 838 | if (deferCallbacks) { 839 | mParentView.post(mSetIdleRunnable); 840 | } else { 841 | setDragState(STATE_IDLE); 842 | } 843 | } 844 | } 845 | 846 | return mDragState == STATE_SETTLING; 847 | } 848 | 849 | /** 850 | * Like all callback events this must happen on the UI thread, but release 851 | * involves some extra semantics. During a release (mReleaseInProgress) is 852 | * the only time it is valid to call {@link #settleCapturedViewAt(int, int)} 853 | * or {@link #flingCapturedView(int, int, int, int)}. 854 | */ 855 | private void dispatchViewReleased(float xvel, float yvel) { 856 | mReleaseInProgress = true; 857 | mCallback.onViewReleased(mCapturedView, xvel, yvel); 858 | mReleaseInProgress = false; 859 | 860 | if (mDragState == STATE_DRAGGING) { 861 | // onViewReleased didn't call a method that would have changed this. 862 | // Go idle. 863 | setDragState(STATE_IDLE); 864 | } 865 | } 866 | 867 | private void clearMotionHistory() { 868 | if (mInitialMotionX == null) { 869 | return; 870 | } 871 | Arrays.fill(mInitialMotionX, 0); 872 | Arrays.fill(mInitialMotionY, 0); 873 | Arrays.fill(mLastMotionX, 0); 874 | Arrays.fill(mLastMotionY, 0); 875 | Arrays.fill(mInitialEdgeTouched, 0); 876 | Arrays.fill(mEdgeDragsInProgress, 0); 877 | Arrays.fill(mEdgeDragsLocked, 0); 878 | mPointersDown = 0; 879 | } 880 | 881 | private void clearMotionHistory(int pointerId) { 882 | if (mInitialMotionX == null) { 883 | return; 884 | } 885 | mInitialMotionX[pointerId] = 0; 886 | mInitialMotionY[pointerId] = 0; 887 | mLastMotionX[pointerId] = 0; 888 | mLastMotionY[pointerId] = 0; 889 | mInitialEdgeTouched[pointerId] = 0; 890 | mEdgeDragsInProgress[pointerId] = 0; 891 | mEdgeDragsLocked[pointerId] = 0; 892 | mPointersDown &= ~(1 << pointerId); 893 | } 894 | 895 | private void ensureMotionHistorySizeForId(int pointerId) { 896 | if (mInitialMotionX == null || mInitialMotionX.length <= pointerId) { 897 | float[] imx = new float[pointerId + 1]; 898 | float[] imy = new float[pointerId + 1]; 899 | float[] lmx = new float[pointerId + 1]; 900 | float[] lmy = new float[pointerId + 1]; 901 | int[] iit = new int[pointerId + 1]; 902 | int[] edip = new int[pointerId + 1]; 903 | int[] edl = new int[pointerId + 1]; 904 | 905 | if (mInitialMotionX != null) { 906 | System.arraycopy(mInitialMotionX, 0, imx, 0, mInitialMotionX.length); 907 | System.arraycopy(mInitialMotionY, 0, imy, 0, mInitialMotionY.length); 908 | System.arraycopy(mLastMotionX, 0, lmx, 0, mLastMotionX.length); 909 | System.arraycopy(mLastMotionY, 0, lmy, 0, mLastMotionY.length); 910 | System.arraycopy(mInitialEdgeTouched, 0, iit, 0, mInitialEdgeTouched.length); 911 | System.arraycopy(mEdgeDragsInProgress, 0, edip, 0, mEdgeDragsInProgress.length); 912 | System.arraycopy(mEdgeDragsLocked, 0, edl, 0, mEdgeDragsLocked.length); 913 | } 914 | 915 | mInitialMotionX = imx; 916 | mInitialMotionY = imy; 917 | mLastMotionX = lmx; 918 | mLastMotionY = lmy; 919 | mInitialEdgeTouched = iit; 920 | mEdgeDragsInProgress = edip; 921 | mEdgeDragsLocked = edl; 922 | } 923 | } 924 | 925 | private void saveInitialMotion(float x, float y, int pointerId) { 926 | ensureMotionHistorySizeForId(pointerId); 927 | mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x; 928 | mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y; 929 | mInitialEdgeTouched[pointerId] = getEdgeTouched((int) x, (int) y); 930 | mPointersDown |= 1 << pointerId; 931 | } 932 | 933 | private void saveLastMotion(MotionEvent ev) { 934 | final int pointerCount = MotionEventCompat.getPointerCount(ev); 935 | for (int i = 0; i < pointerCount; i++) { 936 | final int pointerId = MotionEventCompat.getPointerId(ev, i); 937 | final float x = MotionEventCompat.getX(ev, i); 938 | final float y = MotionEventCompat.getY(ev, i); 939 | mLastMotionX[pointerId] = x; 940 | mLastMotionY[pointerId] = y; 941 | } 942 | } 943 | 944 | /** 945 | * Check if the given pointer ID represents a pointer that is currently down 946 | * (to the best of the ViewDragHelper's knowledge). 947 | *

948 | * The state used to report this information is populated by the methods 949 | * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or 950 | * {@link #processTouchEvent(android.view.MotionEvent)}. If one of these 951 | * methods has not been called for all relevant MotionEvents to track, the 952 | * information reported by this method may be stale or incorrect. 953 | *

954 | * 955 | * @param pointerId pointer ID to check; corresponds to IDs provided by 956 | * MotionEvent 957 | * @return true if the pointer with the given ID is still down 958 | */ 959 | public boolean isPointerDown(int pointerId) { 960 | return (mPointersDown & 1 << pointerId) != 0; 961 | } 962 | 963 | void setDragState(int state) { 964 | if (mDragState != state) { 965 | mDragState = state; 966 | mCallback.onViewDragStateChanged(state); 967 | if (state == STATE_IDLE) { 968 | mCapturedView = null; 969 | } 970 | } 971 | } 972 | 973 | /** 974 | * Attempt to capture the view with the given pointer ID. The callback will 975 | * be involved. This will put us into the "dragging" state. If we've already 976 | * captured this view with this pointer this method will immediately return 977 | * true without consulting the callback. 978 | * 979 | * @param toCapture View to capture 980 | * @param pointerId Pointer to capture with 981 | * @return true if capture was successful 982 | */ 983 | boolean tryCaptureViewForDrag(View toCapture, int pointerId) { 984 | if (toCapture == mCapturedView && mActivePointerId == pointerId) { 985 | // Already done! 986 | return true; 987 | } 988 | if (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) { 989 | mActivePointerId = pointerId; 990 | captureChildView(toCapture, pointerId); 991 | return true; 992 | } 993 | return false; 994 | } 995 | 996 | /** 997 | * Tests scrollability within child views of v given a delta of dx. 998 | * 999 | * @param v View to test for horizontal scrollability 1000 | * @param checkV Whether the view v passed should itself be checked for 1001 | * scrollability (true), or just its children (false). 1002 | * @param dx Delta scrolled in pixels along the X axis 1003 | * @param dy Delta scrolled in pixels along the Y axis 1004 | * @param x X coordinate of the active touch point 1005 | * @param y Y coordinate of the active touch point 1006 | * @return true if child views of v can be scrolled by delta of dx. 1007 | */ 1008 | protected boolean canScroll(View v, boolean checkV, int dx, int dy, int x, int y) { 1009 | if (v instanceof ViewGroup) { 1010 | final ViewGroup group = (ViewGroup) v; 1011 | final int scrollX = v.getScrollX(); 1012 | final int scrollY = v.getScrollY(); 1013 | final int count = group.getChildCount(); 1014 | // Count backwards - let topmost views consume scroll distance 1015 | // first. 1016 | for (int i = count - 1; i >= 0; i--) { 1017 | // TODO: Add versioned support here for transformed views. 1018 | // This will not work for transformed views in Honeycomb+ 1019 | final View child = group.getChildAt(i); 1020 | if (x + scrollX >= child.getLeft() 1021 | && x + scrollX < child.getRight() 1022 | && y + scrollY >= child.getTop() 1023 | && y + scrollY < child.getBottom() 1024 | && canScroll(child, true, dx, dy, x + scrollX - child.getLeft(), y 1025 | + scrollY - child.getTop())) { 1026 | return true; 1027 | } 1028 | } 1029 | } 1030 | 1031 | return checkV 1032 | && (ViewCompat.canScrollHorizontally(v, -dx) || ViewCompat.canScrollVertically(v, 1033 | -dy)); 1034 | } 1035 | 1036 | /** 1037 | * Check if this event as provided to the parent view's 1038 | * onInterceptTouchEvent should cause the parent to intercept the touch 1039 | * event stream. 1040 | * 1041 | * @param ev MotionEvent provided to onInterceptTouchEvent 1042 | * @return true if the parent view should return true from 1043 | * onInterceptTouchEvent 1044 | */ 1045 | public boolean shouldInterceptTouchEvent(MotionEvent ev) { 1046 | final int action = MotionEventCompat.getActionMasked(ev); 1047 | final int actionIndex = MotionEventCompat.getActionIndex(ev); 1048 | 1049 | if (action == MotionEvent.ACTION_DOWN) { 1050 | // Reset things for a new event stream, just in case we didn't get 1051 | // the whole previous stream. 1052 | cancel(); 1053 | } 1054 | 1055 | if (mVelocityTracker == null) { 1056 | mVelocityTracker = VelocityTracker.obtain(); 1057 | } 1058 | mVelocityTracker.addMovement(ev); 1059 | 1060 | switch (action) { 1061 | case MotionEvent.ACTION_DOWN: { 1062 | final float x = ev.getX(); 1063 | final float y = ev.getY(); 1064 | final int pointerId = MotionEventCompat.getPointerId(ev, 0); 1065 | saveInitialMotion(x, y, pointerId); 1066 | 1067 | final View toCapture = findTopChildUnder((int) x, (int) y); 1068 | 1069 | // Catch a settling view if possible. 1070 | if (toCapture == mCapturedView && mDragState == STATE_SETTLING) { 1071 | tryCaptureViewForDrag(toCapture, pointerId); 1072 | } 1073 | 1074 | final int edgesTouched = mInitialEdgeTouched[pointerId]; 1075 | if ((edgesTouched & mTrackingEdges) != 0) { 1076 | mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); 1077 | } 1078 | break; 1079 | } 1080 | 1081 | case MotionEventCompat.ACTION_POINTER_DOWN: { 1082 | final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); 1083 | final float x = MotionEventCompat.getX(ev, actionIndex); 1084 | final float y = MotionEventCompat.getY(ev, actionIndex); 1085 | 1086 | saveInitialMotion(x, y, pointerId); 1087 | 1088 | // A ViewDragHelper can only manipulate one view at a time. 1089 | if (mDragState == STATE_IDLE) { 1090 | final int edgesTouched = mInitialEdgeTouched[pointerId]; 1091 | if ((edgesTouched & mTrackingEdges) != 0) { 1092 | mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); 1093 | } 1094 | } else if (mDragState == STATE_SETTLING) { 1095 | // Catch a settling view if possible. 1096 | final View toCapture = findTopChildUnder((int) x, (int) y); 1097 | if (toCapture == mCapturedView) { 1098 | tryCaptureViewForDrag(toCapture, pointerId); 1099 | } 1100 | } 1101 | break; 1102 | } 1103 | 1104 | case MotionEvent.ACTION_MOVE: { 1105 | // First to cross a touch slop over a draggable view wins. Also 1106 | // report edge drags. 1107 | final int pointerCount = MotionEventCompat.getPointerCount(ev); 1108 | for (int i = 0; i < pointerCount; i++) { 1109 | final int pointerId = MotionEventCompat.getPointerId(ev, i); 1110 | final float x = MotionEventCompat.getX(ev, i); 1111 | final float y = MotionEventCompat.getY(ev, i); 1112 | final float dx = x - mInitialMotionX[pointerId]; 1113 | final float dy = y - mInitialMotionY[pointerId]; 1114 | 1115 | reportNewEdgeDrags(dx, dy, pointerId); 1116 | if (mDragState == STATE_DRAGGING) { 1117 | // Callback might have started an edge drag 1118 | break; 1119 | } 1120 | 1121 | final View toCapture = findTopChildUnder((int) x, (int) y); 1122 | if (toCapture != null && checkTouchSlop(toCapture, dx, dy) 1123 | && tryCaptureViewForDrag(toCapture, pointerId)) { 1124 | break; 1125 | } 1126 | } 1127 | saveLastMotion(ev); 1128 | break; 1129 | } 1130 | 1131 | case MotionEventCompat.ACTION_POINTER_UP: { 1132 | final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); 1133 | clearMotionHistory(pointerId); 1134 | break; 1135 | } 1136 | 1137 | case MotionEvent.ACTION_UP: 1138 | case MotionEvent.ACTION_CANCEL: { 1139 | cancel(); 1140 | break; 1141 | } 1142 | } 1143 | 1144 | return mDragState == STATE_DRAGGING; 1145 | } 1146 | 1147 | /** 1148 | * Process a touch event received by the parent view. This method will 1149 | * dispatch callback events as needed before returning. The parent view's 1150 | * onTouchEvent implementation should call this. 1151 | * 1152 | * @param ev The touch event received by the parent view 1153 | */ 1154 | public void processTouchEvent(MotionEvent ev) { 1155 | final int action = MotionEventCompat.getActionMasked(ev); 1156 | final int actionIndex = MotionEventCompat.getActionIndex(ev); 1157 | 1158 | if (action == MotionEvent.ACTION_DOWN) { 1159 | // Reset things for a new event stream, just in case we didn't get 1160 | // the whole previous stream. 1161 | cancel(); 1162 | } 1163 | 1164 | if (mVelocityTracker == null) { 1165 | mVelocityTracker = VelocityTracker.obtain(); 1166 | } 1167 | mVelocityTracker.addMovement(ev); 1168 | 1169 | switch (action) { 1170 | case MotionEvent.ACTION_DOWN: { 1171 | final float x = ev.getX(); 1172 | final float y = ev.getY(); 1173 | final int pointerId = MotionEventCompat.getPointerId(ev, 0); 1174 | final View toCapture = findTopChildUnder((int) x, (int) y); 1175 | 1176 | saveInitialMotion(x, y, pointerId); 1177 | 1178 | // Since the parent is already directly processing this touch 1179 | // event, 1180 | // there is no reason to delay for a slop before dragging. 1181 | // Start immediately if possible. 1182 | tryCaptureViewForDrag(toCapture, pointerId); 1183 | 1184 | final int edgesTouched = mInitialEdgeTouched[pointerId]; 1185 | if ((edgesTouched & mTrackingEdges) != 0) { 1186 | mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); 1187 | } 1188 | break; 1189 | } 1190 | 1191 | case MotionEventCompat.ACTION_POINTER_DOWN: { 1192 | final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); 1193 | final float x = MotionEventCompat.getX(ev, actionIndex); 1194 | final float y = MotionEventCompat.getY(ev, actionIndex); 1195 | 1196 | saveInitialMotion(x, y, pointerId); 1197 | 1198 | // A ViewDragHelper can only manipulate one view at a time. 1199 | if (mDragState == STATE_IDLE) { 1200 | // If we're idle we can do anything! Treat it like a normal 1201 | // down event. 1202 | 1203 | final View toCapture = findTopChildUnder((int) x, (int) y); 1204 | tryCaptureViewForDrag(toCapture, pointerId); 1205 | 1206 | final int edgesTouched = mInitialEdgeTouched[pointerId]; 1207 | if ((edgesTouched & mTrackingEdges) != 0) { 1208 | mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); 1209 | } 1210 | } else if (isCapturedViewUnder((int) x, (int) y)) { 1211 | // We're still tracking a captured view. If the same view is 1212 | // under this 1213 | // point, we'll swap to controlling it with this pointer 1214 | // instead. 1215 | // (This will still work if we're "catching" a settling 1216 | // view.) 1217 | 1218 | tryCaptureViewForDrag(mCapturedView, pointerId); 1219 | } 1220 | break; 1221 | } 1222 | 1223 | case MotionEvent.ACTION_MOVE: { 1224 | if (mDragState == STATE_DRAGGING) { 1225 | final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId); 1226 | final float x = MotionEventCompat.getX(ev, index); 1227 | final float y = MotionEventCompat.getY(ev, index); 1228 | final int idx = (int) (x - mLastMotionX[mActivePointerId]); 1229 | final int idy = (int) (y - mLastMotionY[mActivePointerId]); 1230 | 1231 | dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy); 1232 | 1233 | saveLastMotion(ev); 1234 | } else { 1235 | // Check to see if any pointer is now over a draggable view. 1236 | final int pointerCount = MotionEventCompat.getPointerCount(ev); 1237 | for (int i = 0; i < pointerCount; i++) { 1238 | final int pointerId = MotionEventCompat.getPointerId(ev, i); 1239 | final float x = MotionEventCompat.getX(ev, i); 1240 | final float y = MotionEventCompat.getY(ev, i); 1241 | final float dx = x - mInitialMotionX[pointerId]; 1242 | final float dy = y - mInitialMotionY[pointerId]; 1243 | 1244 | reportNewEdgeDrags(dx, dy, pointerId); 1245 | if (mDragState == STATE_DRAGGING) { 1246 | // Callback might have started an edge drag. 1247 | break; 1248 | } 1249 | 1250 | final View toCapture = findTopChildUnder((int) x, (int) y); 1251 | if (checkTouchSlop(toCapture, dx, dy) 1252 | && tryCaptureViewForDrag(toCapture, pointerId)) { 1253 | break; 1254 | } 1255 | } 1256 | saveLastMotion(ev); 1257 | } 1258 | break; 1259 | } 1260 | 1261 | case MotionEventCompat.ACTION_POINTER_UP: { 1262 | final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); 1263 | if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) { 1264 | // Try to find another pointer that's still holding on to 1265 | // the captured view. 1266 | int newActivePointer = INVALID_POINTER; 1267 | final int pointerCount = MotionEventCompat.getPointerCount(ev); 1268 | for (int i = 0; i < pointerCount; i++) { 1269 | final int id = MotionEventCompat.getPointerId(ev, i); 1270 | if (id == mActivePointerId) { 1271 | // This one's going away, skip. 1272 | continue; 1273 | } 1274 | 1275 | final float x = MotionEventCompat.getX(ev, i); 1276 | final float y = MotionEventCompat.getY(ev, i); 1277 | if (findTopChildUnder((int) x, (int) y) == mCapturedView 1278 | && tryCaptureViewForDrag(mCapturedView, id)) { 1279 | newActivePointer = mActivePointerId; 1280 | break; 1281 | } 1282 | } 1283 | 1284 | if (newActivePointer == INVALID_POINTER) { 1285 | // We didn't find another pointer still touching the 1286 | // view, release it. 1287 | releaseViewForPointerUp(); 1288 | } 1289 | } 1290 | clearMotionHistory(pointerId); 1291 | break; 1292 | } 1293 | 1294 | case MotionEvent.ACTION_UP: { 1295 | if (mDragState == STATE_DRAGGING) { 1296 | releaseViewForPointerUp(); 1297 | } 1298 | cancel(); 1299 | break; 1300 | } 1301 | 1302 | case MotionEvent.ACTION_CANCEL: { 1303 | if (mDragState == STATE_DRAGGING) { 1304 | dispatchViewReleased(0, 0); 1305 | } 1306 | cancel(); 1307 | break; 1308 | } 1309 | } 1310 | } 1311 | 1312 | private void reportNewEdgeDrags(float dx, float dy, int pointerId) { 1313 | int dragsStarted = 0; 1314 | if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) { 1315 | dragsStarted |= EDGE_LEFT; 1316 | } 1317 | if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) { 1318 | dragsStarted |= EDGE_TOP; 1319 | } 1320 | if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) { 1321 | dragsStarted |= EDGE_RIGHT; 1322 | } 1323 | if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) { 1324 | dragsStarted |= EDGE_BOTTOM; 1325 | } 1326 | 1327 | if (dragsStarted != 0) { 1328 | mEdgeDragsInProgress[pointerId] |= dragsStarted; 1329 | mCallback.onEdgeDragStarted(dragsStarted, pointerId); 1330 | } 1331 | } 1332 | 1333 | private boolean checkNewEdgeDrag(float delta, float odelta, int pointerId, int edge) { 1334 | final float absDelta = Math.abs(delta); 1335 | final float absODelta = Math.abs(odelta); 1336 | 1337 | if ((mInitialEdgeTouched[pointerId] & edge) != edge || (mTrackingEdges & edge) == 0 1338 | || (mEdgeDragsLocked[pointerId] & edge) == edge 1339 | || (mEdgeDragsInProgress[pointerId] & edge) == edge 1340 | || (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) { 1341 | return false; 1342 | } 1343 | if (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) { 1344 | mEdgeDragsLocked[pointerId] |= edge; 1345 | return false; 1346 | } 1347 | return (mEdgeDragsInProgress[pointerId] & edge) == 0 && absDelta > mTouchSlop; 1348 | } 1349 | 1350 | /** 1351 | * Check if we've crossed a reasonable touch slop for the given child view. 1352 | * If the child cannot be dragged along the horizontal or vertical axis, 1353 | * motion along that axis will not count toward the slop check. 1354 | * 1355 | * @param child Child to check 1356 | * @param dx Motion since initial position along X axis 1357 | * @param dy Motion since initial position along Y axis 1358 | * @return true if the touch slop has been crossed 1359 | */ 1360 | private boolean checkTouchSlop(View child, float dx, float dy) { 1361 | if (child == null) { 1362 | return false; 1363 | } 1364 | final boolean checkHorizontal = mCallback.getViewHorizontalDragRange(child) > 0; 1365 | final boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0; 1366 | 1367 | if (checkHorizontal && checkVertical) { 1368 | return dx * dx + dy * dy > mTouchSlop * mTouchSlop; 1369 | } else if (checkHorizontal) { 1370 | return Math.abs(dx) > mTouchSlop; 1371 | } else if (checkVertical) { 1372 | return Math.abs(dy) > mTouchSlop; 1373 | } 1374 | return false; 1375 | } 1376 | 1377 | /** 1378 | * Check if any pointer tracked in the current gesture has crossed the 1379 | * required slop threshold. 1380 | *

1381 | * This depends on internal state populated by 1382 | * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or 1383 | * {@link #processTouchEvent(android.view.MotionEvent)}. You should only 1384 | * rely on the results of this method after all currently available touch 1385 | * data has been provided to one of these two methods. 1386 | *

1387 | * 1388 | * @param directions Combination of direction flags, see 1389 | * {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL}, 1390 | * {@link #DIRECTION_ALL} 1391 | * @return true if the slop threshold has been crossed, false otherwise 1392 | */ 1393 | public boolean checkTouchSlop(int directions) { 1394 | final int count = mInitialMotionX.length; 1395 | for (int i = 0; i < count; i++) { 1396 | if (checkTouchSlop(directions, i)) { 1397 | return true; 1398 | } 1399 | } 1400 | return false; 1401 | } 1402 | 1403 | /** 1404 | * Check if the specified pointer tracked in the current gesture has crossed 1405 | * the required slop threshold. 1406 | *

1407 | * This depends on internal state populated by 1408 | * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or 1409 | * {@link #processTouchEvent(android.view.MotionEvent)}. You should only 1410 | * rely on the results of this method after all currently available touch 1411 | * data has been provided to one of these two methods. 1412 | *

1413 | * 1414 | * @param directions Combination of direction flags, see 1415 | * {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL}, 1416 | * {@link #DIRECTION_ALL} 1417 | * @param pointerId ID of the pointer to slop check as specified by 1418 | * MotionEvent 1419 | * @return true if the slop threshold has been crossed, false otherwise 1420 | */ 1421 | public boolean checkTouchSlop(int directions, int pointerId) { 1422 | if (!isPointerDown(pointerId)) { 1423 | return false; 1424 | } 1425 | 1426 | final boolean checkHorizontal = (directions & DIRECTION_HORIZONTAL) == DIRECTION_HORIZONTAL; 1427 | final boolean checkVertical = (directions & DIRECTION_VERTICAL) == DIRECTION_VERTICAL; 1428 | 1429 | final float dx = mLastMotionX[pointerId] - mInitialMotionX[pointerId]; 1430 | final float dy = mLastMotionY[pointerId] - mInitialMotionY[pointerId]; 1431 | 1432 | if (checkHorizontal && checkVertical) { 1433 | return dx * dx + dy * dy > mTouchSlop * mTouchSlop; 1434 | } else if (checkHorizontal) { 1435 | return Math.abs(dx) > mTouchSlop; 1436 | } else if (checkVertical) { 1437 | return Math.abs(dy) > mTouchSlop; 1438 | } 1439 | return false; 1440 | } 1441 | 1442 | /** 1443 | * Check if any of the edges specified were initially touched in the 1444 | * currently active gesture. If there is no currently active gesture this 1445 | * method will return false. 1446 | * 1447 | * @param edges Edges to check for an initial edge touch. See 1448 | * {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, 1449 | * {@link #EDGE_BOTTOM} and {@link #EDGE_ALL} 1450 | * @return true if any of the edges specified were initially touched in the 1451 | * current gesture 1452 | */ 1453 | public boolean isEdgeTouched(int edges) { 1454 | final int count = mInitialEdgeTouched.length; 1455 | for (int i = 0; i < count; i++) { 1456 | if (isEdgeTouched(edges, i)) { 1457 | return true; 1458 | } 1459 | } 1460 | return false; 1461 | } 1462 | 1463 | /** 1464 | * Check if any of the edges specified were initially touched by the pointer 1465 | * with the specified ID. If there is no currently active gesture or if 1466 | * there is no pointer with the given ID currently down this method will 1467 | * return false. 1468 | * 1469 | * @param edges Edges to check for an initial edge touch. See 1470 | * {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, 1471 | * {@link #EDGE_BOTTOM} and {@link #EDGE_ALL} 1472 | * @return true if any of the edges specified were initially touched in the 1473 | * current gesture 1474 | */ 1475 | public boolean isEdgeTouched(int edges, int pointerId) { 1476 | return isPointerDown(pointerId) && (mInitialEdgeTouched[pointerId] & edges) != 0; 1477 | } 1478 | 1479 | private void releaseViewForPointerUp() { 1480 | mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity); 1481 | final float xvel = clampMag( 1482 | VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId), 1483 | mMinVelocity, mMaxVelocity); 1484 | final float yvel = clampMag( 1485 | VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId), 1486 | mMinVelocity, mMaxVelocity); 1487 | dispatchViewReleased(xvel, yvel); 1488 | } 1489 | 1490 | private void dragTo(int left, int top, int dx, int dy) { 1491 | int clampedX = left; 1492 | int clampedY = top; 1493 | final int oldLeft = mCapturedView.getLeft(); 1494 | final int oldTop = mCapturedView.getTop(); 1495 | if (dx != 0) { 1496 | clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx); 1497 | mCapturedView.offsetLeftAndRight(clampedX - oldLeft); 1498 | } 1499 | if (dy != 0) { 1500 | clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy); 1501 | mCapturedView.offsetTopAndBottom(clampedY - oldTop); 1502 | } 1503 | 1504 | if (dx != 0 || dy != 0) { 1505 | final int clampedDx = clampedX - oldLeft; 1506 | final int clampedDy = clampedY - oldTop; 1507 | mCallback 1508 | .onViewPositionChanged(mCapturedView, clampedX, clampedY, clampedDx, clampedDy); 1509 | } 1510 | } 1511 | 1512 | /** 1513 | * Determine if the currently captured view is under the given point in the 1514 | * parent view's coordinate system. If there is no captured view this method 1515 | * will return false. 1516 | * 1517 | * @param x X position to test in the parent's coordinate system 1518 | * @param y Y position to test in the parent's coordinate system 1519 | * @return true if the captured view is under the given point, false 1520 | * otherwise 1521 | */ 1522 | public boolean isCapturedViewUnder(int x, int y) { 1523 | return isViewUnder(mCapturedView, x, y); 1524 | } 1525 | 1526 | /** 1527 | * Determine if the supplied view is under the given point in the parent 1528 | * view's coordinate system. 1529 | * 1530 | * @param view Child view of the parent to hit test 1531 | * @param x X position to test in the parent's coordinate system 1532 | * @param y Y position to test in the parent's coordinate system 1533 | * @return true if the supplied view is under the given point, false 1534 | * otherwise 1535 | */ 1536 | public boolean isViewUnder(View view, int x, int y) { 1537 | if (view == null) { 1538 | return false; 1539 | } 1540 | return x >= view.getLeft() && x < view.getRight() && y >= view.getTop() 1541 | && y < view.getBottom(); 1542 | } 1543 | 1544 | /** 1545 | * Find the topmost child under the given point within the parent view's 1546 | * coordinate system. The child order is determined using 1547 | * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#getOrderedChildIndex(int)} 1548 | * . 1549 | * 1550 | * @param x X position to test in the parent's coordinate system 1551 | * @param y Y position to test in the parent's coordinate system 1552 | * @return The topmost child view under (x, y) or null if none found. 1553 | */ 1554 | public View findTopChildUnder(int x, int y) { 1555 | final int childCount = mParentView.getChildCount(); 1556 | for (int i = childCount - 1; i >= 0; i--) { 1557 | final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i)); 1558 | if (x >= child.getLeft() && x < child.getRight() && y >= child.getTop() 1559 | && y < child.getBottom()) { 1560 | return child; 1561 | } 1562 | } 1563 | return null; 1564 | } 1565 | 1566 | private int getEdgeTouched(int x, int y) { 1567 | int result = 0; 1568 | 1569 | if (x < mParentView.getLeft() + mEdgeSize) 1570 | result = EDGE_LEFT; 1571 | if (y < mParentView.getTop() + mEdgeSize) 1572 | result = EDGE_TOP; 1573 | if (x > mParentView.getRight() - mEdgeSize) 1574 | result = EDGE_RIGHT; 1575 | if (y > mParentView.getBottom() - mEdgeSize) 1576 | result = EDGE_BOTTOM; 1577 | 1578 | return EDGE_LEFT; 1579 | } 1580 | } 1581 | --------------------------------------------------------------------------------