├── .idea ├── .name ├── copyright │ └── profiles_settings.xml ├── vcs.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── app ├── .gitignore ├── app-release.apk ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── start.jpg │ │ │ │ ├── zhihu.jpg │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── dot_blur.png │ │ │ │ ├── dot_focus.png │ │ │ │ ├── ic_star_white_24dp.png │ │ │ │ ├── ic_file_download_white_24dp.png │ │ │ │ ├── ic_account_circle_white_24dp.png │ │ │ │ ├── item_background_selector_dark.xml │ │ │ │ └── item_background_selector_light.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-hdpi │ │ │ │ ├── ic_star_white_24dp.png │ │ │ │ ├── ic_account_circle_white_24dp.png │ │ │ │ └── ic_file_download_white_24dp.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── ic_star_white_24dp.png │ │ │ │ ├── ic_account_circle_white_24dp.png │ │ │ │ └── ic_file_download_white_24dp.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── ic_star_white_24dp.png │ │ │ │ ├── ic_account_circle_white_24dp.png │ │ │ │ └── ic_file_download_white_24dp.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── ic_star_white_24dp.png │ │ │ │ ├── ic_file_download_white_24dp.png │ │ │ │ └── ic_account_circle_white_24dp.png │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ ├── menu │ │ │ │ └── menu_main.xml │ │ │ ├── layout │ │ │ │ ├── kanner.xml │ │ │ │ ├── menu_item.xml │ │ │ │ ├── splash.xml │ │ │ │ ├── news_layout.xml │ │ │ │ ├── main_news_layout.xml │ │ │ │ ├── kanner_layout.xml │ │ │ │ ├── kanner_content_layout.xml │ │ │ │ ├── news_header.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── news_item.xml │ │ │ │ ├── news_content_layout.xml │ │ │ │ ├── main_news_item.xml │ │ │ │ ├── latest_content_layout.xml │ │ │ │ └── menu.xml │ │ │ ├── drawable │ │ │ │ ├── card_light.xml │ │ │ │ ├── card_light_press.xml │ │ │ │ ├── card_dark_press.xml │ │ │ │ └── card_dark.xml │ │ │ └── anim │ │ │ │ ├── slide_in_from_right.xml │ │ │ │ ├── slide_out_to_left.xml │ │ │ │ └── slide_out_to_left_from_right.xml │ │ ├── java │ │ │ └── krelve │ │ │ │ └── app │ │ │ │ └── kuaihu │ │ │ │ ├── model │ │ │ │ ├── NewsListItem.java │ │ │ │ ├── StoriesEntity.java │ │ │ │ ├── Content.java │ │ │ │ ├── Before.java │ │ │ │ ├── Latest.java │ │ │ │ └── News.java │ │ │ │ ├── db │ │ │ │ ├── WebCacheDbHelper.java │ │ │ │ └── CacheDbHelper.java │ │ │ │ ├── util │ │ │ │ ├── Constant.java │ │ │ │ ├── HttpUtils.java │ │ │ │ └── PreUtils.java │ │ │ │ ├── fragment │ │ │ │ ├── BaseFragment.java │ │ │ │ ├── NewsFragment.java │ │ │ │ ├── MenuFragment.java │ │ │ │ └── MainFragment.java │ │ │ │ ├── Kpplication.java │ │ │ │ ├── adapter │ │ │ │ ├── NewsItemAdapter.java │ │ │ │ └── MainNewsItemAdapter.java │ │ │ │ ├── view │ │ │ │ ├── RevealBackgroundView.java │ │ │ │ └── Kanner.java │ │ │ │ └── activity │ │ │ │ ├── SplashActivity.java │ │ │ │ ├── NewsContentActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── LatestContentActivity.java │ │ ├── AndroidManifest.xml │ │ └── assets │ │ │ └── css │ │ │ └── news.css │ └── androidTest │ │ └── java │ │ └── krelve │ │ └── app │ │ └── kuaihu │ │ └── ApplicationTest.java ├── proguard-rules.pro ├── build.gradle └── app.iml ├── settings.gradle ├── 知乎日报API.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── .gitattributes ├── gradle.properties ├── KuaiHu.iml ├── README.md ├── gradlew.bat └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | KuaiHu -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /知乎日报API.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/知乎日报API.md -------------------------------------------------------------------------------- /app/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/app-release.apk -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/start.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/mipmap-xhdpi/start.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/zhihu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/mipmap-xhdpi/zhihu.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/dot_blur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xhdpi/dot_blur.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/dot_focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xhdpi/dot_focus.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_star_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-hdpi/ic_star_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_star_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-mdpi/ic_star_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_star_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xhdpi/ic_star_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_star_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xxhdpi/ic_star_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_star_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xxxhdpi/ic_star_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_account_circle_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-hdpi/ic_account_circle_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_file_download_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-hdpi/ic_file_download_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_account_circle_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-mdpi/ic_account_circle_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_file_download_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-mdpi/ic_file_download_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_file_download_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xhdpi/ic_file_download_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_account_circle_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xhdpi/ic_account_circle_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_account_circle_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xxhdpi/ic_account_circle_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_file_download_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xxhdpi/ic_file_download_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_file_download_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xxxhdpi/ic_file_download_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_account_circle_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YiuChoi/KuaiHu/master/app/src/main/res/drawable-xxxhdpi/ic_account_circle_white_24dp.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 高仿知乎日报 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 11 11:31:18 CST 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.4-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item_background_selector_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/item_background_selector_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/androidTest/java/krelve/app/kuaihu/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu; 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 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/model/NewsListItem.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.model; 2 | 3 | public class NewsListItem { 4 | private String title; 5 | private String id; 6 | 7 | public String getTitle() { 8 | return title; 9 | } 10 | 11 | public void setTitle(String title) { 12 | this.title = title; 13 | } 14 | 15 | public String getId() { 16 | return id; 17 | } 18 | 19 | public void setId(String id) { 20 | this.id = id; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/kanner.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/menu_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/news_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/card_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_news_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/card_light_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/card_dark_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/card_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/kanner_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/db/WebCacheDbHelper.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.db; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | /** 8 | * Created by wwjun.wang on 2015/8/19. 9 | */ 10 | public class WebCacheDbHelper extends SQLiteOpenHelper { 11 | public WebCacheDbHelper(Context context, int version) { 12 | super(context, "webCache.db", null, version); 13 | } 14 | 15 | 16 | @Override 17 | public void onCreate(SQLiteDatabase db) { 18 | db.execSQL("create table if not exists Cache (id INTEGER primary key autoincrement,newsId INTEGER unique,json text)"); 19 | } 20 | 21 | @Override 22 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/db/CacheDbHelper.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.db; 2 | 3 | import android.content.Context; 4 | import android.database.DatabaseErrorHandler; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.database.sqlite.SQLiteOpenHelper; 7 | 8 | /** 9 | * Created by wwjun.wang on 2015/8/19. 10 | */ 11 | public class CacheDbHelper extends SQLiteOpenHelper { 12 | public CacheDbHelper(Context context, int version) { 13 | super(context, "cache.db", null, version); 14 | } 15 | 16 | 17 | @Override 18 | public void onCreate(SQLiteDatabase db) { 19 | db.execSQL("create table if not exists CacheList (id INTEGER primary key autoincrement,date INTEGER unique,json text)"); 20 | } 21 | 22 | @Override 23 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/kanner_content_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/util/Constant.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.util; 2 | 3 | /** 4 | * Created by wwjun.wang on 2015/8/11. 5 | */ 6 | public class Constant { 7 | public static final String BASEURL = "http://news-at.zhihu.com/api/4/"; 8 | public static final String START = "start-image/1080*1776"; 9 | public static final String THEMES = "themes"; 10 | public static final String LATESTNEWS = "news/latest"; 11 | public static final String BEFORE = "news/before/"; 12 | public static final String THEMENEWS = "theme/"; 13 | public static final String CONTENT = "news/"; 14 | public static final int TOPIC = 131; 15 | public static final String START_LOCATION = "start_location"; 16 | public static final String CACHE = "cache"; 17 | public static final int LATEST_COLUMN = Integer.MAX_VALUE; 18 | public static final int BASE_COLUMN = 100000000; 19 | 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 -------------------------------------------------------------------------------- /app/src/main/res/layout/news_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 24 | 25 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "krelve.app.kuaihu" 9 | minSdkVersion 15 10 | targetSdkVersion 22 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 | compile 'com.android.support:appcompat-v7:22.2.1' 25 | compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4' 26 | compile 'com.loopj.android:android-async-http:1.4.8' 27 | compile 'com.google.code.gson:gson:2.3.1' 28 | compile 'com.android.support:design:22.2.1' 29 | compile 'com.android.support:support-v4:22.2.1' 30 | } 31 | -------------------------------------------------------------------------------- /KuaiHu.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_from_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_to_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 24 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_to_left_from_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 24 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/fragment/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.fragment; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | /** 12 | * Created by wwjun.wang on 2015/8/12. 13 | */ 14 | public abstract class BaseFragment extends Fragment { 15 | protected Activity mActivity; 16 | 17 | @Nullable 18 | @Override 19 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 20 | mActivity = getActivity(); 21 | return initView(inflater, container, savedInstanceState); 22 | } 23 | 24 | @Override 25 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 26 | super.onActivityCreated(savedInstanceState); 27 | initData(); 28 | } 29 | 30 | protected void initData() { 31 | 32 | } 33 | 34 | @Override 35 | public void onDestroy() { 36 | super.onDestroy(); 37 | mActivity = null; 38 | } 39 | 40 | protected abstract View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/util/HttpUtils.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.util; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | import com.loopj.android.http.AsyncHttpClient; 8 | import com.loopj.android.http.ResponseHandlerInterface; 9 | 10 | /** 11 | * Created by wwjun.wang on 2015/8/11. 12 | */ 13 | public class HttpUtils { 14 | private static AsyncHttpClient client = new AsyncHttpClient(); 15 | 16 | public static void get(String url, ResponseHandlerInterface responseHandler) { 17 | client.get(Constant.BASEURL + url, responseHandler); 18 | } 19 | 20 | public static void getImage(String url, ResponseHandlerInterface responseHandler) { 21 | client.get(url, responseHandler); 22 | } 23 | 24 | public static boolean isNetworkConnected(Context context) { 25 | if (context != null) { 26 | ConnectivityManager mConnectivityManager = (ConnectivityManager) context 27 | .getSystemService(Context.CONNECTIVITY_SERVICE); 28 | NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); 29 | if (mNetworkInfo != null) { 30 | return mNetworkInfo.isAvailable(); 31 | } 32 | } 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #BEBEBE 4 | @android:color/holo_blue_dark 5 | @android:color/black 6 | #FFF0F0F0 7 | #DD000000 8 | #ff666666 9 | #CCFFFFFF 10 | #FF333333 11 | @android:color/holo_blue_dark 12 | @android:color/black 13 | @android:color/white 14 | #CCFFFFFF 15 | #FFF0F0F0 16 | #FF111111 17 | @android:color/white 18 | #FF222222 19 | #FF000000 20 | @android:color/darker_gray 21 | @android:color/darker_gray 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/util/PreUtils.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.util; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.preference.PreferenceManager; 7 | 8 | import java.util.Deque; 9 | 10 | /** 11 | * Created by wwjun.wang on 2015/8/19. 12 | */ 13 | public class PreUtils { 14 | public static void putStringToDefault(Context context, String key, String value) { 15 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); 16 | sp.edit().putString(key, value).commit(); 17 | } 18 | 19 | public static String getStringFromDefault(Context context, String key, String defValue) { 20 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); 21 | return sp.getString(key, defValue); 22 | } 23 | 24 | /* public static void putStringTo(String name, Context context, String key, String value) { 25 | SharedPreferences sp = context.getSharedPreferences(name, Context.MODE_PRIVATE); 26 | sp.edit().putString(key, value).commit(); 27 | } 28 | 29 | public static String getStringFrom(String name, Context context, String key, String defValue) { 30 | SharedPreferences sp = context.getSharedPreferences(name, Context.MODE_PRIVATE); 31 | return sp.getString(key, defValue); 32 | }*/ 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 29 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 20 | 21 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##高仿知乎日报 2 | ###APK下载 3 | [百度网盘](http://pan.baidu.com/s/1nt1RI7B "高仿知乎日报") 或 [CSDN](http://download.csdn.net/detail/wo350546294/9029347 "高仿知乎日报") 4 | ###教程 5 | [高仿知乎日报(一)](http://krelve.com/android/123.html "高仿知乎日报(一)") 6 | [高仿知乎日报(二)](http://krelve.com/android/125.html "高仿知乎日报(二)") 7 | [高仿知乎日报(三)](http://krelve.com/android/126.html "高仿知乎日报(三)") 8 | [高仿知乎日报(四)](http://krelve.com/android/127.html "高仿知乎日报(四)") 9 | [高仿知乎日报(五)](http://krelve.com/android/128.html "高仿知乎日报(五)") 10 | [高仿知乎日报(六)](http://krelve.com/android/129.html "高仿知乎日报(六)") 11 | ###知乎日报API 12 | [知乎日报API](https://github.com/iKrelve/KuaiHu/blob/master/%E7%9F%A5%E4%B9%8E%E6%97%A5%E6%8A%A5API.md "知乎日报API") 13 | [来自izzyleung,在此表示感谢](https://github.com/izzyleung/ZhihuDailyPurify "izzyleung") 14 | ###已完成功能 15 | * 启动动画与启动图片的获取 16 | * 文章类型的展示与缓存 17 | * 文章列表的展示与缓存 18 | * 文章内容的展示 19 | * 今日热闻界面的轮播控件 20 | * 下拉刷新 21 | * 文章浏览到底部时自动加载更多 22 | * 标记已读文章 23 | * 自动缓存文章 24 | * 夜间模式 25 | 26 | ###依赖的开源项目 27 | [Gson](https://github.com/google/gson "Gson") 28 | [Android-Universal-Image-Loader](https://github.com/nostra13/Android-Universal-Image-Loader "Android-Universal-Image-Loader") 29 | [android-async-http](https://github.com/loopj/android-async-http "android-async-http") 30 | 31 | ###依赖的官方支持包 32 | * **com.android.support:support-v4:22.2.1** 33 | * **com.android.support:appcompat-v7:22.2.1** 34 | * **com.android.support:design:22.2.1** 35 | 36 | ###亮点 37 | * **Android Design Support Library的使用** 38 | * Snackbar 39 | * CoordinatorLayout 40 | * AppBarLayout 41 | * CollapsingToolbarLayout 42 | * **Activity切换的Reveal效果** 43 | * [来自InstaMaterial](https://github.com/frogermcs/InstaMaterial "InstaMaterial") 44 | 45 | ###效果 46 | ![](http://7xjs0n.com1.z0.glb.clouddn.com/zhihu_final.gif) 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 20 | 21 | 24 | 25 | 28 | 29 | 33 | 34 | 35 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/news_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 24 | 25 | 26 | 33 | 34 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/Kpplication.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.media.Image; 6 | 7 | import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; 8 | import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator; 9 | import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; 10 | import com.nostra13.universalimageloader.core.ImageLoader; 11 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; 12 | import com.nostra13.universalimageloader.core.assist.QueueProcessingType; 13 | import com.nostra13.universalimageloader.utils.StorageUtils; 14 | 15 | import java.io.File; 16 | 17 | /** 18 | * Created by wwjun.wang on 2015/8/11. 19 | */ 20 | public class Kpplication extends Application { 21 | 22 | @Override 23 | public void onCreate() { 24 | super.onCreate(); 25 | initImageLoader(getApplicationContext()); 26 | } 27 | 28 | 29 | private void initImageLoader(Context context) { 30 | File cacheDir = StorageUtils.getCacheDirectory(context); 31 | /* ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder( 32 | context).threadPoolSize(3) 33 | .threadPriority(Thread.NORM_PRIORITY - 2) 34 | .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) 35 | .denyCacheImageMultipleSizesInMemory() 36 | .diskCacheFileNameGenerator(new Md5FileNameGenerator()) 37 | .tasksProcessingOrder(QueueProcessingType.LIFO) 38 | .diskCache(new UnlimitedDiskCache(cacheDir)).writeDebugLogs() 39 | .build();*/ 40 | ImageLoaderConfiguration config = ImageLoaderConfiguration.createDefault(context); 41 | ImageLoader.getInstance().init(config); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/model/StoriesEntity.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by wwjun.wang on 2015/8/14. 8 | */ 9 | public class StoriesEntity implements Serializable{ 10 | /** 11 | * id : 7047795 12 | * title : 央视说要干预男男性行为,具体是怎么干预法? 13 | * ga_prefix : 081310 14 | * images : ["http://pic3.zhimg.com/fe27abc8f094510f2d3b4f3706108b56.jpg"] 15 | * type : 0 16 | */ 17 | private int id; 18 | private String title; 19 | // private String ga_prefix; 20 | private List images; 21 | private int type; 22 | 23 | public void setId(int id) { 24 | this.id = id; 25 | } 26 | 27 | public void setTitle(String title) { 28 | this.title = title; 29 | } 30 | 31 | // public void setGa_prefix(String ga_prefix) { 32 | // this.ga_prefix = ga_prefix; 33 | // } 34 | 35 | public void setImages(List images) { 36 | this.images = images; 37 | } 38 | 39 | public void setType(int type) { 40 | this.type = type; 41 | } 42 | 43 | public int getId() { 44 | return id; 45 | } 46 | 47 | public String getTitle() { 48 | return title; 49 | } 50 | 51 | // public String getGa_prefix() { 52 | // return ga_prefix; 53 | // } 54 | 55 | public List getImages() { 56 | return images; 57 | } 58 | 59 | public int getType() { 60 | return type; 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return "StoriesEntity{" + 66 | "id=" + id + 67 | ", title='" + title + '\'' + 68 | /*", ga_prefix='" + ga_prefix + '\'' +*/ 69 | ", images=" + images + 70 | ", type=" + type + 71 | '}'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/layout/news_content_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | 23 | 24 | 29 | 30 | 31 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_news_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 27 | 28 | 33 | 34 | 35 | 42 | 43 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /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/res/layout/latest_content_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | 22 | 23 | 33 | 34 | 42 | 43 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/model/Content.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by wwjun.wang on 2015/8/17. 7 | */ 8 | public class Content { 9 | 10 | /** 11 | * id : 7053854 12 | * recommenders : [{"avatar":"http://pic3.zhimg.com/bbb689a7a_m.jpg"}] 13 | * body : 这是html文本 14 | * title : 野生动物从不刷牙,有点担心他们的牙齿出问题 15 | * ga_prefix : 081710 16 | * share_url : http://daily.zhihu.com/story/7053854 17 | * js : [] 18 | * image : http://pic1.zhimg.com/3d8395f01761c77e87b673d0806191a4.jpg 19 | * type : 0 20 | * css : ["http://news.at.zhihu.com/css/news_qa.auto.css?v=016bb"] 21 | * image_source : 站酷海洛创意 22 | */ 23 | private int id; 24 | private List recommenders; 25 | private String body; 26 | private String title; 27 | private String ga_prefix; 28 | private String share_url; 29 | private String image; 30 | private int type; 31 | private List css; 32 | private String image_source; 33 | 34 | public void setId(int id) { 35 | this.id = id; 36 | } 37 | 38 | public void setRecommenders(List recommenders) { 39 | this.recommenders = recommenders; 40 | } 41 | 42 | public void setBody(String body) { 43 | this.body = body; 44 | } 45 | 46 | public void setTitle(String title) { 47 | this.title = title; 48 | } 49 | 50 | public void setGa_prefix(String ga_prefix) { 51 | this.ga_prefix = ga_prefix; 52 | } 53 | 54 | public void setShare_url(String share_url) { 55 | this.share_url = share_url; 56 | } 57 | 58 | 59 | public void setImage(String image) { 60 | this.image = image; 61 | } 62 | 63 | public void setType(int type) { 64 | this.type = type; 65 | } 66 | 67 | public void setCss(List css) { 68 | this.css = css; 69 | } 70 | 71 | public void setImage_source(String image_source) { 72 | this.image_source = image_source; 73 | } 74 | 75 | public int getId() { 76 | return id; 77 | } 78 | 79 | public List getRecommenders() { 80 | return recommenders; 81 | } 82 | 83 | public String getBody() { 84 | return body; 85 | } 86 | 87 | public String getTitle() { 88 | return title; 89 | } 90 | 91 | public String getGa_prefix() { 92 | return ga_prefix; 93 | } 94 | 95 | public String getShare_url() { 96 | return share_url; 97 | } 98 | 99 | 100 | public String getImage() { 101 | return image; 102 | } 103 | 104 | public int getType() { 105 | return type; 106 | } 107 | 108 | public List getCss() { 109 | return css; 110 | } 111 | 112 | public String getImage_source() { 113 | return image_source; 114 | } 115 | 116 | public static class RecommendersEntity { 117 | /** 118 | * avatar : http://pic3.zhimg.com/bbb689a7a_m.jpg 119 | */ 120 | private String avatar; 121 | 122 | public void setAvatar(String avatar) { 123 | this.avatar = avatar; 124 | } 125 | 126 | public String getAvatar() { 127 | return avatar; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/res/layout/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 22 | 23 | 27 | 28 | 37 | 38 | 39 | 44 | 45 | 56 | 57 | 68 | 69 | 70 | 71 | 82 | 83 | 90 | 91 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/adapter/NewsItemAdapter.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.FrameLayout; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | import android.widget.TextView; 12 | 13 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 14 | import com.nostra13.universalimageloader.core.ImageLoader; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import krelve.app.kuaihu.R; 20 | import krelve.app.kuaihu.activity.MainActivity; 21 | import krelve.app.kuaihu.model.StoriesEntity; 22 | import krelve.app.kuaihu.util.Constant; 23 | import krelve.app.kuaihu.util.PreUtils; 24 | 25 | /** 26 | * Created by wwjun.wang on 2015/8/14. 27 | */ 28 | public class NewsItemAdapter extends BaseAdapter { 29 | private List entities; 30 | private Context context; 31 | private ImageLoader mImageloader; 32 | private DisplayImageOptions options; 33 | private boolean isLight; 34 | 35 | public NewsItemAdapter(Context context, List items) { 36 | this.context = context; 37 | entities = items; 38 | isLight = ((MainActivity) context).isLight(); 39 | mImageloader = ImageLoader.getInstance(); 40 | options = new DisplayImageOptions.Builder() 41 | .cacheInMemory(true) 42 | .cacheOnDisk(true) 43 | .build(); 44 | } 45 | 46 | @Override 47 | public int getCount() { 48 | return entities.size(); 49 | } 50 | 51 | @Override 52 | public Object getItem(int position) { 53 | return entities.get(position); 54 | } 55 | 56 | @Override 57 | public long getItemId(int position) { 58 | return position; 59 | } 60 | 61 | @Override 62 | public View getView(int position, View convertView, ViewGroup parent) { 63 | ViewHolder viewHolder = null; 64 | if (convertView == null) { 65 | viewHolder = new ViewHolder(); 66 | convertView = LayoutInflater.from(context).inflate(R.layout.news_item, parent, false); 67 | viewHolder.tv_title = (TextView) convertView.findViewById(R.id.tv_title); 68 | viewHolder.iv_title = (ImageView) convertView.findViewById(R.id.iv_title); 69 | convertView.setTag(viewHolder); 70 | } else { 71 | viewHolder = (ViewHolder) convertView.getTag(); 72 | } 73 | 74 | String readSeq = PreUtils.getStringFromDefault(context, "read", ""); 75 | if (readSeq.contains(entities.get(position).getId() + "")) { 76 | viewHolder.tv_title.setTextColor(context.getResources().getColor(R.color.clicked_tv_textcolor)); 77 | } else { 78 | viewHolder.tv_title.setTextColor(context.getResources().getColor(isLight ? android.R.color.black : android.R.color.white)); 79 | } 80 | 81 | ((LinearLayout) viewHolder.iv_title.getParent().getParent().getParent()).setBackgroundColor(context.getResources().getColor(isLight ? R.color.light_news_item : R.color.dark_news_item)); 82 | ((FrameLayout) viewHolder.tv_title.getParent().getParent()).setBackgroundResource(isLight ? R.drawable.item_background_selector_light : R.drawable.item_background_selector_dark); 83 | StoriesEntity entity = entities.get(position); 84 | viewHolder.tv_title.setText(entity.getTitle()); 85 | if (entity.getImages() != null) { 86 | viewHolder.iv_title.setVisibility(View.VISIBLE); 87 | mImageloader.displayImage(entity.getImages().get(0), viewHolder.iv_title, options); 88 | } else { 89 | viewHolder.iv_title.setVisibility(View.GONE); 90 | } 91 | return convertView; 92 | } 93 | 94 | public void updateTheme() { 95 | isLight = ((MainActivity) context).isLight(); 96 | notifyDataSetChanged(); 97 | } 98 | 99 | public static class ViewHolder { 100 | TextView tv_title; 101 | ImageView iv_title; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/view/RevealBackgroundView.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.view; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ObjectAnimator; 6 | import android.annotation.TargetApi; 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.os.Build; 12 | import android.util.AttributeSet; 13 | import android.view.View; 14 | import android.view.animation.AccelerateInterpolator; 15 | import android.view.animation.Interpolator; 16 | 17 | /** 18 | * Created by Miroslaw Stanek on 18.01.15. 19 | */ 20 | public class RevealBackgroundView extends View { 21 | public static final int STATE_NOT_STARTED = 0; 22 | public static final int STATE_FILL_STARTED = 1; 23 | public static final int STATE_FINISHED = 2; 24 | 25 | private static final Interpolator INTERPOLATOR = new AccelerateInterpolator(); 26 | private static final int FILL_TIME = 600; 27 | 28 | private int state = STATE_NOT_STARTED; 29 | 30 | private Paint fillPaint; 31 | private int currentRadius; 32 | ObjectAnimator revealAnimator; 33 | 34 | private int startLocationX; 35 | private int startLocationY; 36 | 37 | 38 | private OnStateChangeListener onStateChangeListener; 39 | 40 | public RevealBackgroundView(Context context) { 41 | super(context); 42 | init(); 43 | } 44 | 45 | public RevealBackgroundView(Context context, AttributeSet attrs) { 46 | super(context, attrs); 47 | init(); 48 | } 49 | 50 | public RevealBackgroundView(Context context, AttributeSet attrs, int defStyleAttr) { 51 | super(context, attrs, defStyleAttr); 52 | init(); 53 | } 54 | 55 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 56 | public RevealBackgroundView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 57 | super(context, attrs, defStyleAttr, defStyleRes); 58 | init(); 59 | } 60 | 61 | private void init() { 62 | fillPaint = new Paint(); 63 | fillPaint.setStyle(Paint.Style.FILL); 64 | fillPaint.setColor(Color.WHITE); 65 | } 66 | 67 | public void setFillPaintColor(int color) { 68 | fillPaint.setColor(color); 69 | } 70 | 71 | public void startFromLocation(int[] tapLocationOnScreen) { 72 | changeState(STATE_FILL_STARTED); 73 | startLocationX = tapLocationOnScreen[0]; 74 | startLocationY = tapLocationOnScreen[1]; 75 | revealAnimator = ObjectAnimator.ofInt(this, "currentRadius", 0, getWidth() + getHeight()).setDuration(FILL_TIME); 76 | revealAnimator.setInterpolator(INTERPOLATOR); 77 | revealAnimator.addListener(new AnimatorListenerAdapter() { 78 | @Override 79 | public void onAnimationEnd(Animator animation) { 80 | changeState(STATE_FINISHED); 81 | } 82 | }); 83 | revealAnimator.start(); 84 | } 85 | 86 | public void setToFinishedFrame() { 87 | changeState(STATE_FINISHED); 88 | invalidate(); 89 | } 90 | 91 | @Override 92 | protected void onDraw(Canvas canvas) { 93 | if (state == STATE_FINISHED) { 94 | canvas.drawRect(0, 0, getWidth(), getHeight(), fillPaint); 95 | } else { 96 | canvas.drawCircle(startLocationX, startLocationY, currentRadius, fillPaint); 97 | } 98 | } 99 | 100 | private void changeState(int state) { 101 | if (this.state == state) { 102 | return; 103 | } 104 | 105 | this.state = state; 106 | if (onStateChangeListener != null) { 107 | onStateChangeListener.onStateChange(state); 108 | } 109 | } 110 | 111 | public void setOnStateChangeListener(OnStateChangeListener onStateChangeListener) { 112 | this.onStateChangeListener = onStateChangeListener; 113 | } 114 | 115 | public void setCurrentRadius(int radius) { 116 | this.currentRadius = radius; 117 | invalidate(); 118 | } 119 | 120 | public static interface OnStateChangeListener { 121 | void onStateChange(int state); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/model/Before.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by wwjun.wang on 2015/8/14. 7 | */ 8 | public class Before { 9 | 10 | /** 11 | * stories : [{"id":1747159,"title":"深夜食堂 · 我的张曼妮","ga_prefix":"111822","images":["http://p4.zhimg.com/7b/c8/7bc8ef5947b069513c51e4b9521b5c82.jpg"],"type":0},{"id":1858551,"title":"清朝皇帝上朝的时候说的是满语还是汉语?","ga_prefix":"111822","images":["http://p3.zhimg.com/21/0c/210c7b63b931932fa7a1e62bf0113e7b.jpg"],"type":0},{"id":1848791,"title":"淘宝上那些适合送爸妈的东西","ga_prefix":"111821","images":["http://p4.zhimg.com/7c/d1/7cd1496541c7964b2cf8614b9fa664b0.jpg"],"type":0},{"id":1849914,"title":"恋爱里的男子汉,迎头专心刷榜才是正经事","ga_prefix":"111820","images":["http://p2.zhimg.com/11/05/1105cfa3d12f3539ef35fa603614ed92.jpg"],"type":0},{"id":1854693,"title":"鸡蛋黄和蛋清长成了的话分别是鸡的什么部位?","ga_prefix":"111819","images":["http://p4.zhimg.com/cf/1f/cf1fd58f22d3c5fd2fd2a5543d70f81d.jpg"],"type":0},{"id":1861205,"title":"鲜柚游戏周报\r\n回顾一周 iOS 精品游戏","ga_prefix":"111818","images":["http://p2.zhimg.com/e3/d1/e3d15e98b3db498d53d9ed1b85d2fab5.jpg"],"type":0},{"id":1858917,"multipic":true,"title":"吃很重要 · 第一口就开始 high 了(多图)","ga_prefix":"111818","images":["http://p2.zhimg.com/14/3b/143bd74ec7a0299b76d17e6b095799aa.jpg"],"type":0},{"id":1856401,"title":"追女孩教练传授:妹子玩手机的话,你就也玩手机,挺好的","ga_prefix":"111818","images":["http://p2.zhimg.com/51/32/51324fa89e1aba7a337e20e98c9664f1.jpg"],"type":0},{"id":1854400,"title":"最美应用 · 给你一种新邮箱Molto","ga_prefix":"111817","images":["http://p3.zhimg.com/f0/97/f0973d30830eed315d46b531f38719cf.jpg"],"type":0},{"id":1848590,"title":"谁在维护比特币的核心算法?","ga_prefix":"111817","images":["http://p1.zhimg.com/d6/11/d611dd7d57d144621779ec36c8df42fb.jpg"],"type":0},{"id":1847175,"title":"离岸金融:一种光明正大的钻空子行为","ga_prefix":"111816","images":["http://p3.zhimg.com/f8/70/f870fac8fea14e56d2cddf926a4800f2.jpg"],"type":0},{"id":1846706,"title":"银泰全面支持支付宝钱包付款,两个初学者的第一次","ga_prefix":"111815","images":["http://p1.zhimg.com/d4/30/d430ba0d8d9e51482b6a0bd8ff5ef6ee.jpg"],"type":0},{"id":1846781,"title":"仅售 179 美元,Moto G 为什么定价这么低","ga_prefix":"111814","images":["http://p1.zhimg.com/4b/8c/4b8c8f9c40f08fa9a1a830095131c67c.jpg"],"type":0},{"id":1844934,"title":"导演张一白:青春小说到青春电影,中间有层面纱","ga_prefix":"111813","images":["http://p3.zhimg.com/2c/ce/2cce90676f6841e01ab683384f4daaf0.jpg"],"type":0},{"id":1838196,"title":"知天下 · 圆顶事实上是宗教建筑中常用的一种造型","ga_prefix":"111812","images":["http://p4.zhimg.com/39/ff/39ff45effc9f6083bb8da5a6f768eaa2.jpg"],"type":0},{"id":1844302,"title":"PrimeSense:苹果正在试图收购这个革命性体感控制设备","ga_prefix":"111811","images":["http://p1.zhimg.com/80/26/802617acf921694c7a2e732008e6c2cf.jpg"],"type":0},{"id":1844263,"title":"家庭用 100M 光纤使用什么无线路由器才能发挥最大网速?","ga_prefix":"111810","images":["http://p2.zhimg.com/a6/42/a6423122d959de347cc8a8c61d150c21.jpg"],"type":0},{"id":1843578,"title":"金融产品也有物流,并且好处多多","ga_prefix":"111809","images":["http://p2.zhimg.com/52/94/52941a00e16bffffe480e19c387d07d9.jpg"],"type":0},{"id":1839454,"title":"召回六十多万辆车,大众继续焦头烂额处理变速箱问题","ga_prefix":"111807","images":["http://p1.zhimg.com/86/79/86799f8608bf39171b78456675a9f4f0.jpg"],"type":0},{"id":1843290,"title":"全方位冬日晨跑注意事项已供上,假设你已起床","ga_prefix":"111807","images":["http://p4.zhimg.com/3a/dd/3adda8a964695f3d0c84944fbb676cda.jpg"],"type":0},{"id":1838920,"title":"独处时才是了解自己的最好机会,你上完厕所会冲吗?","ga_prefix":"111807","images":["http://p2.zhimg.com/a8/a6/a8a677d04d27a96cdb457e6c1a430d68.jpg"],"type":0},{"id":1843557,"title":"李宗盛:既然青春留不住,不如听大叔讲故事","ga_prefix":"111807","images":["http://p4.zhimg.com/c5/7d/c57d1d0ee1ba83df700982a4f8e5ac26.jpg"],"type":0},{"id":1839693,"title":"创业公司财务怎么做,绝大多数创业初期年轻人不知道这个","ga_prefix":"111807","images":["http://p3.zhimg.com/b7/b2/b7b223eaa3a6daaf680f266973803c75.jpg"],"type":0},{"id":1841395,"title":"瞎扯 · 如何正确地吐槽","ga_prefix":"111806","images":["http://p3.zhimg.com/21/32/21328ba459bee7961dc71de398002638.jpg"],"type":0}] 12 | * date : 20131118 13 | */ 14 | private List stories; 15 | private String date; 16 | 17 | public void setStories(List stories) { 18 | this.stories = stories; 19 | } 20 | 21 | public void setDate(String date) { 22 | this.date = date; 23 | } 24 | 25 | public List getStories() { 26 | return stories; 27 | } 28 | 29 | public String getDate() { 30 | return date; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/model/Latest.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by wwjun.wang on 2015/8/12. 7 | */ 8 | public class Latest { 9 | 10 | 11 | /** 12 | * top_stories : [{"id":7048089,"title":"发生类似天津爆炸事故时,该如何自救?","ga_prefix":"081309","image":"http://pic4.zhimg.com/494dafbd64c141fd023d4e58b3343fcb.jpg","type":0},{"id":7047383,"title":"每卖一辆车亏 4000 美元,这事儿跟「iPhone 成本仅几百元」挺像","ga_prefix":"081307","image":"http://pic1.zhimg.com/40e0f21292df0e8512385f191e71ad14.jpg","type":0},{"id":7047795,"title":"央视说要干预男男性行为,具体是怎么干预法?","ga_prefix":"081310","image":"http://pic4.zhimg.com/89f0bca7d4ccf70bd747f3675adc18eb.jpg","type":0},{"id":7047071,"title":"美国人最爱买的车第一名是它,第二名是它,第三名,还是它\u2026\u2026","ga_prefix":"081307","image":"http://pic1.zhimg.com/9c00c482251e82fa8e0b957fa9ceb334.jpg","type":0},{"id":7046751,"title":"今晚的修破斯哒是 · 小李子","ga_prefix":"081219","image":"http://pic3.zhimg.com/bc5f63634d9c9832da8593ac64ebb7d6.jpg","type":0}] 13 | * stories : [{"id":7047795,"title":"央视说要干预男男性行为,具体是怎么干预法?","ga_prefix":"081310","images":["http://pic3.zhimg.com/fe27abc8f094510f2d3b4f3706108b56.jpg"],"type":0},{"id":7048089,"title":"发生类似天津爆炸事故时,该如何自救?","ga_prefix":"081309","images":["http://pic1.zhimg.com/eabb48a57948dc405429d0c0185c7950.jpg"],"type":0},{"id":7047188,"title":"分析了一下,发现这几个中国城市不光物价高,而且收入低","ga_prefix":"081308","images":["http://pic2.zhimg.com/7ce0dfe918b11069bc857421876e6609.jpg"],"type":0},{"id":7047383,"title":"每卖一辆车亏 4000 美元,这事儿跟「iPhone 成本仅几百元」挺像","ga_prefix":"081307","images":["http://pic2.zhimg.com/b8319323c8f8e3ec0ccc80d4745305a9.jpg"],"type":0},{"id":7047071,"title":"美国人最爱买的车第一名是它,第二名是它,第三名,还是它\u2026\u2026","ga_prefix":"081307","images":["http://pic2.zhimg.com/822b9ce452e8d48e6d32b83d4c1ea6e9.jpg"],"type":0},{"id":7047484,"title":"理论上就业率对工资很重要,但是在中国没这么回事","ga_prefix":"081307","images":["http://pic4.zhimg.com/d08952ed50050efdcee33203a4225ba3.jpg"],"type":0},{"id":7047181,"title":"瞎扯 · 如何正确地吐槽","ga_prefix":"081306","images":["http://pic4.zhimg.com/1dd6304067619318034671af9cf26803.jpg"],"type":0}] 14 | * date : 20150813 15 | */ 16 | private List top_stories; 17 | private List stories; 18 | private String date; 19 | 20 | public void setTop_stories(List top_stories) { 21 | this.top_stories = top_stories; 22 | } 23 | 24 | public void setStories(List stories) { 25 | this.stories = stories; 26 | } 27 | 28 | public void setDate(String date) { 29 | this.date = date; 30 | } 31 | 32 | public List getTop_stories() { 33 | return top_stories; 34 | } 35 | 36 | public List getStories() { 37 | return stories; 38 | } 39 | 40 | public String getDate() { 41 | return date; 42 | } 43 | 44 | public static class TopStoriesEntity { 45 | /** 46 | * id : 7048089 47 | * title : 发生类似天津爆炸事故时,该如何自救? 48 | * ga_prefix : 081309 49 | * image : http://pic4.zhimg.com/494dafbd64c141fd023d4e58b3343fcb.jpg 50 | * type : 0 51 | */ 52 | private int id; 53 | private String title; 54 | private String ga_prefix; 55 | private String image; 56 | private int type; 57 | 58 | public void setId(int id) { 59 | this.id = id; 60 | } 61 | 62 | public void setTitle(String title) { 63 | this.title = title; 64 | } 65 | 66 | public void setGa_prefix(String ga_prefix) { 67 | this.ga_prefix = ga_prefix; 68 | } 69 | 70 | public void setImage(String image) { 71 | this.image = image; 72 | } 73 | 74 | public void setType(int type) { 75 | this.type = type; 76 | } 77 | 78 | public int getId() { 79 | return id; 80 | } 81 | 82 | public String getTitle() { 83 | return title; 84 | } 85 | 86 | public String getGa_prefix() { 87 | return ga_prefix; 88 | } 89 | 90 | public String getImage() { 91 | return image; 92 | } 93 | 94 | public int getType() { 95 | return type; 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | return "TopStoriesEntity{" + 101 | "id=" + id + 102 | ", title='" + title + '\'' + 103 | ", ga_prefix='" + ga_prefix + '\'' + 104 | ", image='" + image + '\'' + 105 | ", type=" + type + 106 | '}'; 107 | } 108 | } 109 | 110 | 111 | @Override 112 | public String toString() { 113 | return "Latest{" + 114 | "top_stories=" + top_stories + 115 | ", stories=" + stories + 116 | ", date='" + date + '\'' + 117 | '}'; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/adapter/MainNewsItemAdapter.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.adapter; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.FrameLayout; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | import android.widget.TextView; 13 | 14 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 15 | import com.nostra13.universalimageloader.core.ImageLoader; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import krelve.app.kuaihu.R; 21 | import krelve.app.kuaihu.activity.MainActivity; 22 | import krelve.app.kuaihu.model.StoriesEntity; 23 | import krelve.app.kuaihu.util.Constant; 24 | import krelve.app.kuaihu.util.PreUtils; 25 | 26 | /** 27 | * Created by wwjun.wang on 2015/8/13. 28 | */ 29 | public class MainNewsItemAdapter extends BaseAdapter { 30 | private List entities; 31 | private Context context; 32 | private ImageLoader mImageloader; 33 | private DisplayImageOptions options; 34 | private boolean isLight; 35 | 36 | public MainNewsItemAdapter(Context context) { 37 | this.context = context; 38 | this.entities = new ArrayList<>(); 39 | mImageloader = ImageLoader.getInstance(); 40 | options = new DisplayImageOptions.Builder() 41 | .cacheInMemory(true) 42 | .cacheOnDisk(true) 43 | .build(); 44 | isLight = ((MainActivity) context).isLight(); 45 | } 46 | 47 | public void addList(List items) { 48 | this.entities.addAll(items); 49 | notifyDataSetChanged(); 50 | } 51 | 52 | @Override 53 | public int getCount() { 54 | return entities.size(); 55 | } 56 | 57 | @Override 58 | public Object getItem(int position) { 59 | return entities.get(position); 60 | } 61 | 62 | @Override 63 | public long getItemId(int position) { 64 | return position; 65 | } 66 | 67 | @Override 68 | public View getView(int position, View convertView, ViewGroup parent) { 69 | ViewHolder viewHolder = null; 70 | if (convertView == null) { 71 | viewHolder = new ViewHolder(); 72 | convertView = LayoutInflater.from(context).inflate(R.layout.main_news_item, parent, false); 73 | viewHolder.tv_topic = (TextView) convertView.findViewById(R.id.tv_topic); 74 | viewHolder.tv_title = (TextView) convertView.findViewById(R.id.tv_title); 75 | viewHolder.iv_title = (ImageView) convertView.findViewById(R.id.iv_title); 76 | convertView.setTag(viewHolder); 77 | } else { 78 | viewHolder = (ViewHolder) convertView.getTag(); 79 | } 80 | String readSeq = PreUtils.getStringFromDefault(context, "read", ""); 81 | if (readSeq.contains(entities.get(position).getId() + "")) { 82 | viewHolder.tv_title.setTextColor(context.getResources().getColor(R.color.clicked_tv_textcolor)); 83 | } else { 84 | viewHolder.tv_title.setTextColor(context.getResources().getColor(isLight ? android.R.color.black : android.R.color.white)); 85 | } 86 | ((LinearLayout) viewHolder.iv_title.getParent().getParent().getParent()).setBackgroundColor(context.getResources().getColor(isLight ? R.color.light_news_item : R.color.dark_news_item)); 87 | viewHolder.tv_topic.setTextColor(context.getResources().getColor(isLight ? R.color.light_news_topic : R.color.dark_news_topic)); 88 | StoriesEntity entity = entities.get(position); 89 | if (entity.getType() == Constant.TOPIC) { 90 | ((FrameLayout) viewHolder.tv_topic.getParent()).setBackgroundColor(Color.TRANSPARENT); 91 | viewHolder.tv_title.setVisibility(View.GONE); 92 | viewHolder.iv_title.setVisibility(View.GONE); 93 | viewHolder.tv_topic.setVisibility(View.VISIBLE); 94 | viewHolder.tv_topic.setText(entity.getTitle()); 95 | } else { 96 | ((FrameLayout) viewHolder.tv_topic.getParent()).setBackgroundResource(isLight ? R.drawable.item_background_selector_light : R.drawable.item_background_selector_dark); 97 | viewHolder.tv_topic.setVisibility(View.GONE); 98 | viewHolder.tv_title.setVisibility(View.VISIBLE); 99 | viewHolder.iv_title.setVisibility(View.VISIBLE); 100 | viewHolder.tv_title.setText(entity.getTitle()); 101 | mImageloader.displayImage(entity.getImages().get(0), viewHolder.iv_title, options); 102 | } 103 | return convertView; 104 | } 105 | 106 | public void updateTheme() { 107 | isLight = ((MainActivity) context).isLight(); 108 | notifyDataSetChanged(); 109 | } 110 | 111 | public static class ViewHolder { 112 | TextView tv_topic; 113 | TextView tv_title; 114 | ImageView iv_title; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/activity/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.activity; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.BitmapFactory; 6 | import android.os.Bundle; 7 | import android.view.Window; 8 | import android.view.WindowManager; 9 | import android.view.animation.Animation; 10 | import android.view.animation.ScaleAnimation; 11 | import android.widget.ImageView; 12 | import android.widget.Toast; 13 | 14 | import com.loopj.android.http.AsyncHttpResponseHandler; 15 | import com.loopj.android.http.BinaryHttpResponseHandler; 16 | 17 | import org.apache.http.Header; 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | import java.io.File; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | 25 | import krelve.app.kuaihu.R; 26 | import krelve.app.kuaihu.util.Constant; 27 | import krelve.app.kuaihu.util.HttpUtils; 28 | 29 | /** 30 | * Created by wwjun.wang on 2015/8/11. 31 | */ 32 | public class SplashActivity extends Activity { 33 | private ImageView iv_start; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | requestWindowFeature(Window.FEATURE_NO_TITLE); 39 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 40 | setContentView(R.layout.splash); 41 | iv_start = (ImageView) findViewById(R.id.iv_start); 42 | initImage(); 43 | 44 | 45 | } 46 | 47 | private void initImage() { 48 | File dir = getFilesDir(); 49 | final File imgFile = new File(dir, "start.jpg"); 50 | if (imgFile.exists()) { 51 | iv_start.setImageBitmap(BitmapFactory.decodeFile(imgFile.getAbsolutePath())); 52 | } else { 53 | iv_start.setImageResource(R.mipmap.start); 54 | } 55 | 56 | final ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 1.2f, 1.0f, 1.2f, 57 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 58 | 0.5f); 59 | scaleAnim.setFillAfter(true); 60 | scaleAnim.setDuration(3000); 61 | scaleAnim.setAnimationListener(new Animation.AnimationListener() { 62 | @Override 63 | public void onAnimationStart(Animation animation) { 64 | 65 | } 66 | 67 | @Override 68 | public void onAnimationEnd(Animation animation) { 69 | if (HttpUtils.isNetworkConnected(SplashActivity.this)) { 70 | HttpUtils.get(Constant.START, new AsyncHttpResponseHandler() { 71 | @Override 72 | public void onSuccess(int i, Header[] headers, byte[] bytes) { 73 | try { 74 | JSONObject jsonObject = new JSONObject(new String(bytes)); 75 | String url = jsonObject.getString("img"); 76 | HttpUtils.getImage(url, new BinaryHttpResponseHandler() { 77 | @Override 78 | public void onSuccess(int i, Header[] headers, byte[] bytes) { 79 | saveImage(imgFile, bytes); 80 | startActivity(); 81 | } 82 | 83 | @Override 84 | public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) { 85 | startActivity(); 86 | } 87 | }); 88 | 89 | } catch (JSONException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | 94 | @Override 95 | public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) { 96 | startActivity(); 97 | } 98 | }); 99 | } else { 100 | Toast.makeText(SplashActivity.this, "没有网络连接!", Toast.LENGTH_LONG).show(); 101 | startActivity(); 102 | } 103 | } 104 | 105 | @Override 106 | public void onAnimationRepeat(Animation animation) { 107 | 108 | } 109 | }); 110 | iv_start.startAnimation(scaleAnim); 111 | 112 | } 113 | 114 | private void startActivity() { 115 | Intent intent = new Intent(SplashActivity.this, MainActivity.class); 116 | startActivity(intent); 117 | overridePendingTransition(android.R.anim.fade_in, 118 | android.R.anim.fade_out); 119 | finish(); 120 | } 121 | 122 | public void saveImage(File file, byte[] bytes) { 123 | try { 124 | if (file.exists()) { 125 | file.delete(); 126 | } 127 | FileOutputStream fos = new FileOutputStream(file); 128 | fos.write(bytes); 129 | fos.flush(); 130 | fos.close(); 131 | } catch (IOException e) { 132 | e.printStackTrace(); 133 | } 134 | 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /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 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/activity/NewsContentActivity.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.activity; 2 | 3 | import android.database.Cursor; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.os.Bundle; 6 | import android.support.design.widget.CoordinatorLayout; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.Toolbar; 9 | import android.view.View; 10 | import android.view.ViewTreeObserver; 11 | import android.webkit.WebSettings; 12 | import android.webkit.WebView; 13 | 14 | import com.google.gson.Gson; 15 | import com.loopj.android.http.TextHttpResponseHandler; 16 | import com.nostra13.universalimageloader.core.ImageLoader; 17 | 18 | import org.apache.http.Header; 19 | 20 | import krelve.app.kuaihu.R; 21 | import krelve.app.kuaihu.db.WebCacheDbHelper; 22 | import krelve.app.kuaihu.model.Content; 23 | import krelve.app.kuaihu.model.StoriesEntity; 24 | import krelve.app.kuaihu.util.Constant; 25 | import krelve.app.kuaihu.util.HttpUtils; 26 | import krelve.app.kuaihu.view.RevealBackgroundView; 27 | 28 | /** 29 | * Created by wwjun.wang on 2015/8/17. 30 | */ 31 | public class NewsContentActivity extends AppCompatActivity implements RevealBackgroundView.OnStateChangeListener { 32 | private WebView mWebView; 33 | private StoriesEntity entity; 34 | private Content content; 35 | private RevealBackgroundView vRevealBackground; 36 | private CoordinatorLayout coordinatorLayout; 37 | private WebCacheDbHelper dbHelper; 38 | private boolean isLight; 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.news_content_layout); 44 | dbHelper = new WebCacheDbHelper(this, 1); 45 | isLight = getIntent().getBooleanExtra("isLight", true); 46 | coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorLayout); 47 | coordinatorLayout.setVisibility(View.INVISIBLE); 48 | vRevealBackground = (RevealBackgroundView) findViewById(R.id.revealBackgroundView); 49 | entity = (StoriesEntity) getIntent().getSerializableExtra("entity"); 50 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 51 | toolbar.setTitle("享受阅读的乐趣"); 52 | toolbar.setBackgroundColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 53 | setSupportActionBar(toolbar); 54 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 55 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 56 | @Override 57 | public void onClick(View v) { 58 | onBackPressed(); 59 | } 60 | }); 61 | 62 | mWebView = (WebView) findViewById(R.id.webview); 63 | mWebView.getSettings().setJavaScriptEnabled(true); 64 | mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); 65 | // 开启DOM storage API 功能 66 | mWebView.getSettings().setDomStorageEnabled(true); 67 | // 开启database storage API功能 68 | mWebView.getSettings().setDatabaseEnabled(true); 69 | // 开启Application Cache功能 70 | mWebView.getSettings().setAppCacheEnabled(true); 71 | if (HttpUtils.isNetworkConnected(this)) { 72 | HttpUtils.get(Constant.CONTENT + entity.getId(), new TextHttpResponseHandler() { 73 | @Override 74 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { 75 | 76 | } 77 | 78 | @Override 79 | public void onSuccess(int statusCode, Header[] headers, String responseString) { 80 | SQLiteDatabase db = dbHelper.getWritableDatabase(); 81 | responseString = responseString.replaceAll("'", "''"); 82 | db.execSQL("replace into Cache(newsId,json) values(" + entity.getId() + ",'" + responseString + "')"); 83 | db.close(); 84 | parseJson(responseString); 85 | } 86 | }); 87 | } else { 88 | SQLiteDatabase db = dbHelper.getReadableDatabase(); 89 | Cursor cursor = db.rawQuery("select * from Cache where newsId = " + entity.getId(), null); 90 | if (cursor.moveToFirst()) { 91 | String json = cursor.getString(cursor.getColumnIndex("json")); 92 | parseJson(json); 93 | } 94 | cursor.close(); 95 | db.close(); 96 | } 97 | 98 | setupRevealBackground(savedInstanceState); 99 | } 100 | 101 | private void parseJson(String responseString) { 102 | Gson gson = new Gson(); 103 | content = gson.fromJson(responseString, Content.class); 104 | String css = ""; 105 | String html = "" + css + "" + content.getBody() + ""; 106 | html = html.replace("
", ""); 107 | mWebView.loadDataWithBaseURL("x-data://base", html, "text/html", "UTF-8", null); 108 | } 109 | 110 | 111 | private void setupRevealBackground(Bundle savedInstanceState) { 112 | vRevealBackground.setOnStateChangeListener(this); 113 | if (savedInstanceState == null) { 114 | final int[] startingLocation = getIntent().getIntArrayExtra(Constant.START_LOCATION); 115 | vRevealBackground.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 116 | @Override 117 | public boolean onPreDraw() { 118 | vRevealBackground.getViewTreeObserver().removeOnPreDrawListener(this); 119 | vRevealBackground.startFromLocation(startingLocation); 120 | return true; 121 | } 122 | }); 123 | } else { 124 | vRevealBackground.setToFinishedFrame(); 125 | } 126 | } 127 | 128 | @Override 129 | public void onStateChange(int state) { 130 | if (RevealBackgroundView.STATE_FINISHED == state) { 131 | coordinatorLayout.setVisibility(View.VISIBLE); 132 | } 133 | } 134 | 135 | @Override 136 | public void onBackPressed() { 137 | finish(); 138 | overridePendingTransition(0, R.anim.slide_out_to_left); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/model/News.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by wwjun.wang on 2015/8/14. 7 | */ 8 | public class News { 9 | 10 | /** 11 | * stories : [{"id":7050241,"title":"为什么二次元粉丝物理都很难及格?","images":["http://pic4.zhimg.com/a012398ff96c6999fe17a59c177e125b_t.jpg"],"type":2},{"id":7048880,"title":"我们实际在做的 VS 我们心里想做的(爱)","images":["http://pic2.zhimg.com/e79b827b810ea3d62dea7d9ea1a20409_t.jpg"],"type":1},{"id":7049075,"title":"为什么说在中国当消防员是最非人的工作?","images":["http://pic2.zhimg.com/48d8d425bd958bca0e2c8b32947909d9_t.jpg"],"type":2},{"id":7047328,"title":"那些给你造成心理阴影的极品室友!不能忍!","images":["http://pic1.zhimg.com/9248fc025e7bd0beb1f12d8ade879b00_t.jpg"],"type":2},{"id":7046196,"title":"你便便的形状到底意味着什么?","images":["http://pic4.zhimg.com/5653042f76a56b7a16ca70fcf3d24553_t.jpg"],"type":2},{"id":7045556,"title":"6 张意大利小方饺的黄色动图,精神身体双重满足","images":["http://pic1.zhimg.com/09254cb2584580676d00476850c1e3dc_t.jpg"],"type":2},{"id":7044522,"title":"14 个喜怒哀乐的冷知识,比微表情靠谱","images":["http://pic3.zhimg.com/8c56946657296930560b84f9ea96b32a_t.jpg"],"type":2},{"id":7043374,"title":"现在日本妹纸最流行的卖萌妆是找到喝醉的节奏","images":["http://pic3.zhimg.com/0444507622888a2247c0c4a9918b6c46_t.jpg"],"type":2},{"id":7041376,"title":"12 个本周冷知识,我们可能生活在虚拟世界里","images":["http://pic1.zhimg.com/df6c1806637b200a20cf1d1dcb80a110_t.jpg"],"type":2},{"id":7041059,"title":"11 张本周最热节操图,第一视角看冲浪是啥样?","images":["http://pic4.zhimg.com/0651f2f4686239d3152b3fc89e0a3eeb_t.jpg"],"type":2},{"id":7040333,"title":"你不知道的匡威,其实清朝就有了!","images":["http://pic3.zhimg.com/571155680c0f741731ebc6845031726a_t.jpg"],"type":2},{"id":7039746,"title":"9 件男朋友送的崩溃礼物,明明说好了爱我的!","images":["http://pic3.zhimg.com/c16230b4f9c1243488be32b120b96876_t.jpg"],"type":2},{"id":7037920,"title":"小时候家里穷是怎样一种体验?","images":["http://pic4.zhimg.com/f0dd2e448c148205f9d06841e628a32f_t.jpg"],"type":2},{"id":7036785,"title":"12 个关于绿可乐的真相,不是薄荷味的噢","images":["http://pic1.zhimg.com/a3a60aecb5a14fe5eecaa64ccd7eeee0_t.jpg"],"type":2},{"id":7035648,"title":"VICE 呈现:一块钱广告位","images":["http://pic4.zhimg.com/f53275ee42ce45fd49ff1c7823acdd3f_t.jpg"],"type":2},{"id":7035630,"title":"我只有一个愿望:《银魂》永不完结!","images":["http://pic1.zhimg.com/cbc90f48b2eb76dade41a6afb9e659c8_t.jpg"],"type":2},{"id":7033692,"title":"一份来自 \u201c云南山歌\u201d 里的毕业旅行指南 | VICE 中国","images":["http://pic4.zhimg.com/9da0d6540ecce8bd931696fb7a17ed63_t.jpg"],"type":1},{"id":7034628,"title":"在我国当单身女是一种什么体验?","images":["http://pic4.zhimg.com/3662a8259039e1bcfa0767c64b94997f_t.jpg"],"type":2},{"id":7033873,"title":"6 个去美国冻卵子前必须清楚的医学风险","images":["http://pic3.zhimg.com/be8d675e6270b54e3ad18b027433bae2_t.jpg"],"type":2}] 12 | * color : 8307764 13 | * description : 为你发现最有趣的新鲜事,建议在 WiFi 下查看 14 | * name : 不许无聊 15 | * background : http://pic1.zhimg.com/a5128188ed788005ad50840a42079c41.jpg 16 | * image : http://pic3.zhimg.com/da1fcaf6a02d1223d130d5b106e828b9.jpg 17 | * editors : [{"id":70,"bio":"微在 Wezeit 主编","name":"益康糯米","avatar":"http://pic4.zhimg.com/068311926_m.jpg","url":"http://www.zhihu.com/people/wezeit"},{"id":69,"bio":"VICE 中国编辑","name":"狐狸","avatar":"http://pic2.zhimg.com/6dbfe3c7f_m.jpg","url":"http://www.zhihu.com/people/firefoxwae"},{"id":71,"bio":"明月般俊朗","name":"顾惜朝","avatar":"http://pic4.zhimg.com/43d10de2b46c918a9ffe5d0472947b67_m.jpg","url":"http://www.zhihu.com/people/kuangzhou"}] 18 | * image_source : 19 | */ 20 | private List stories; 21 | private int color; 22 | private String description; 23 | private String name; 24 | private String background; 25 | private String image; 26 | private List editors; 27 | private String image_source; 28 | 29 | public void setStories(List stories) { 30 | this.stories = stories; 31 | } 32 | 33 | public void setColor(int color) { 34 | this.color = color; 35 | } 36 | 37 | public void setDescription(String description) { 38 | this.description = description; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public void setBackground(String background) { 46 | this.background = background; 47 | } 48 | 49 | public void setImage(String image) { 50 | this.image = image; 51 | } 52 | 53 | public void setEditors(List editors) { 54 | this.editors = editors; 55 | } 56 | 57 | public void setImage_source(String image_source) { 58 | this.image_source = image_source; 59 | } 60 | 61 | public List getStories() { 62 | return stories; 63 | } 64 | 65 | public int getColor() { 66 | return color; 67 | } 68 | 69 | public String getDescription() { 70 | return description; 71 | } 72 | 73 | public String getName() { 74 | return name; 75 | } 76 | 77 | public String getBackground() { 78 | return background; 79 | } 80 | 81 | public String getImage() { 82 | return image; 83 | } 84 | 85 | public List getEditors() { 86 | return editors; 87 | } 88 | 89 | public String getImage_source() { 90 | return image_source; 91 | } 92 | 93 | 94 | public static class EditorsEntity { 95 | /** 96 | * id : 70 97 | * bio : 微在 Wezeit 主编 98 | * name : 益康糯米 99 | * avatar : http://pic4.zhimg.com/068311926_m.jpg 100 | * url : http://www.zhihu.com/people/wezeit 101 | */ 102 | private int id; 103 | private String bio; 104 | private String name; 105 | private String avatar; 106 | private String url; 107 | 108 | public void setId(int id) { 109 | this.id = id; 110 | } 111 | 112 | public void setBio(String bio) { 113 | this.bio = bio; 114 | } 115 | 116 | public void setName(String name) { 117 | this.name = name; 118 | } 119 | 120 | public void setAvatar(String avatar) { 121 | this.avatar = avatar; 122 | } 123 | 124 | public void setUrl(String url) { 125 | this.url = url; 126 | } 127 | 128 | public int getId() { 129 | return id; 130 | } 131 | 132 | public String getBio() { 133 | return bio; 134 | } 135 | 136 | public String getName() { 137 | return name; 138 | } 139 | 140 | public String getAvatar() { 141 | return avatar; 142 | } 143 | 144 | public String getUrl() { 145 | return url; 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.activity; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.SharedPreferences; 5 | import android.graphics.Color; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.preference.PreferenceManager; 9 | import android.support.design.widget.Snackbar; 10 | import android.support.v4.widget.DrawerLayout; 11 | import android.support.v4.widget.SwipeRefreshLayout; 12 | import android.support.v7.app.ActionBarDrawerToggle; 13 | import android.support.v7.app.AppCompatActivity; 14 | import android.support.v7.widget.Toolbar; 15 | import android.view.Gravity; 16 | import android.view.Menu; 17 | import android.view.MenuItem; 18 | import android.view.Window; 19 | import android.view.WindowManager; 20 | import android.widget.FrameLayout; 21 | 22 | import krelve.app.kuaihu.R; 23 | import krelve.app.kuaihu.db.CacheDbHelper; 24 | import krelve.app.kuaihu.fragment.MainFragment; 25 | import krelve.app.kuaihu.fragment.MenuFragment; 26 | import krelve.app.kuaihu.fragment.NewsFragment; 27 | 28 | public class MainActivity extends AppCompatActivity { 29 | private FrameLayout fl_content; 30 | private MenuFragment menu_fragment; 31 | private DrawerLayout mDrawerLayout; 32 | private SwipeRefreshLayout sr; 33 | private long firstTime; 34 | private String curId; 35 | private Toolbar toolbar; 36 | private boolean isLight; 37 | private CacheDbHelper dbHelper; 38 | private SharedPreferences sp; 39 | 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | sp = PreferenceManager.getDefaultSharedPreferences(this); 46 | dbHelper = new CacheDbHelper(this, 1); 47 | isLight = sp.getBoolean("isLight", true); 48 | initView(); 49 | loadLatest(); 50 | } 51 | 52 | public void loadLatest() { 53 | getSupportFragmentManager().beginTransaction(). 54 | setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left). 55 | replace(R.id.fl_content, new MainFragment(), "latest"). 56 | commit(); 57 | curId = "latest"; 58 | } 59 | 60 | public void setCurId(String id) { 61 | curId = id; 62 | } 63 | 64 | private void initView() { 65 | toolbar = (Toolbar) findViewById(R.id.toolbar); 66 | toolbar.setBackgroundColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 67 | setSupportActionBar(toolbar); 68 | setStatusBarColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 69 | 70 | sr = (SwipeRefreshLayout) findViewById(R.id.sr); 71 | sr.setColorSchemeResources(android.R.color.holo_blue_bright, 72 | android.R.color.holo_green_light, 73 | android.R.color.holo_orange_light, 74 | android.R.color.holo_red_light); 75 | 76 | sr.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 77 | @Override 78 | public void onRefresh() { 79 | replaceFragment(); 80 | sr.setRefreshing(false); 81 | } 82 | }); 83 | fl_content = (FrameLayout) findViewById(R.id.fl_content); 84 | mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout); 85 | final ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, 86 | toolbar, R.string.app_name, R.string.app_name); 87 | mDrawerLayout.setDrawerListener(drawerToggle); 88 | drawerToggle.syncState(); 89 | } 90 | 91 | public void replaceFragment() { 92 | if (curId.equals("latest")) { 93 | getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left) 94 | .replace(R.id.fl_content, 95 | new MainFragment(), "latest").commit(); 96 | } else { 97 | 98 | } 99 | 100 | } 101 | 102 | public void closeMenu() { 103 | mDrawerLayout.closeDrawers(); 104 | } 105 | 106 | public void setSwipeRefreshEnable(boolean enable) { 107 | sr.setEnabled(enable); 108 | } 109 | 110 | public void setToolbarTitle(String text) { 111 | toolbar.setTitle(text); 112 | } 113 | 114 | 115 | @Override 116 | public boolean onCreateOptionsMenu(Menu menu) { 117 | getMenuInflater().inflate(R.menu.menu_main, menu); 118 | menu.getItem(0).setTitle(sp.getBoolean("isLight", true) ? "夜间模式" : "日间模式"); 119 | return true; 120 | } 121 | 122 | @Override 123 | public boolean onOptionsItemSelected(MenuItem item) { 124 | int id = item.getItemId(); 125 | if (id == R.id.action_mode) { 126 | isLight = !isLight; 127 | // TODO: 15-8-29 现在只有这个activity有夜间模式,打开日报详情还不是啊 128 | item.setTitle(isLight?"夜间模式":"日间模式"); 129 | toolbar.setBackgroundColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 130 | setStatusBarColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 131 | if (curId.equals("latest")) { 132 | ((MainFragment) getSupportFragmentManager().findFragmentByTag("latest")).updateTheme(); 133 | } else { 134 | ((NewsFragment) getSupportFragmentManager().findFragmentByTag("news")).updateTheme(); 135 | } 136 | ((MenuFragment) getSupportFragmentManager().findFragmentById(R.id.menu_fragment)).updateTheme(); 137 | sp.edit().putBoolean("isLight", isLight).apply(); 138 | } 139 | 140 | return super.onOptionsItemSelected(item); 141 | } 142 | 143 | public boolean isLight() { 144 | return isLight; 145 | } 146 | 147 | public CacheDbHelper getCacheDbHelper() { 148 | return dbHelper; 149 | } 150 | 151 | @Override 152 | public void onBackPressed() { 153 | if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) { 154 | closeMenu(); 155 | } else { 156 | long secondTime = System.currentTimeMillis(); 157 | if (secondTime - firstTime > 2000) { 158 | Snackbar sb = Snackbar.make(fl_content, "再按一次退出", Snackbar.LENGTH_SHORT); 159 | sb.getView().setBackgroundColor(getResources().getColor(isLight ? android.R.color.holo_blue_dark : android.R.color.black)); 160 | sb.show(); 161 | firstTime = secondTime; 162 | } else { 163 | finish(); 164 | } 165 | } 166 | 167 | } 168 | 169 | 170 | @TargetApi(21) 171 | private void setStatusBarColor(int statusBarColor) { 172 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 173 | // If both system bars are black, we can remove these from our layout, 174 | // removing or shrinking the SurfaceFlinger overlay required for our views. 175 | Window window = this.getWindow(); 176 | if (statusBarColor == Color.BLACK && window.getNavigationBarColor() == Color.BLACK) { 177 | window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 178 | } else { 179 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 180 | } 181 | window.setStatusBarColor(statusBarColor); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/fragment/NewsFragment.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.fragment; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.os.Bundle; 8 | import android.support.design.widget.Snackbar; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.AbsListView; 13 | import android.widget.AdapterView; 14 | import android.widget.ImageView; 15 | import android.widget.ListView; 16 | import android.widget.TextView; 17 | 18 | import com.google.gson.Gson; 19 | import com.loopj.android.http.TextHttpResponseHandler; 20 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 21 | import com.nostra13.universalimageloader.core.ImageLoader; 22 | 23 | import org.apache.http.Header; 24 | 25 | import java.util.ArrayList; 26 | 27 | import krelve.app.kuaihu.R; 28 | import krelve.app.kuaihu.activity.LatestContentActivity; 29 | import krelve.app.kuaihu.activity.MainActivity; 30 | import krelve.app.kuaihu.activity.NewsContentActivity; 31 | import krelve.app.kuaihu.adapter.NewsItemAdapter; 32 | import krelve.app.kuaihu.db.CacheDbHelper; 33 | import krelve.app.kuaihu.model.News; 34 | import krelve.app.kuaihu.model.StoriesEntity; 35 | import krelve.app.kuaihu.util.Constant; 36 | import krelve.app.kuaihu.util.HttpUtils; 37 | import krelve.app.kuaihu.util.PreUtils; 38 | 39 | /** 40 | * Created by wwjun.wang on 2015/8/14. 41 | */ 42 | @SuppressLint("ValidFragment") 43 | public class NewsFragment extends BaseFragment { 44 | private ImageLoader mImageLoader; 45 | private ListView lv_news; 46 | private ImageView iv_title; 47 | private TextView tv_title; 48 | private String urlId; 49 | private News news; 50 | private NewsItemAdapter mAdapter; 51 | private String title; 52 | 53 | 54 | public NewsFragment(String id, String title) { 55 | urlId = id; 56 | this.title = title; 57 | } 58 | 59 | @Override 60 | protected View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 61 | ((MainActivity) mActivity).setToolbarTitle(title); 62 | View view = inflater.inflate(R.layout.news_layout, container, false); 63 | mImageLoader = ImageLoader.getInstance(); 64 | lv_news = (ListView) view.findViewById(R.id.lv_news); 65 | View header = LayoutInflater.from(mActivity).inflate( 66 | R.layout.news_header, lv_news, false); 67 | iv_title = (ImageView) header.findViewById(R.id.iv_title); 68 | tv_title = (TextView) header.findViewById(R.id.tv_title); 69 | lv_news.addHeaderView(header); 70 | lv_news.setOnItemClickListener(new AdapterView.OnItemClickListener() { 71 | 72 | @Override 73 | public void onItemClick(AdapterView parent, View view, 74 | int position, long id) { 75 | int[] startingLocation = new int[2]; 76 | view.getLocationOnScreen(startingLocation); 77 | startingLocation[0] += view.getWidth() / 2; 78 | StoriesEntity entity = (StoriesEntity) parent.getAdapter().getItem(position); 79 | Intent intent = new Intent(mActivity, NewsContentActivity.class); 80 | intent.putExtra(Constant.START_LOCATION, startingLocation); 81 | intent.putExtra("entity", entity); 82 | intent.putExtra("isLight", ((MainActivity) mActivity).isLight()); 83 | 84 | String readSequence = PreUtils.getStringFromDefault(mActivity, "read", ""); 85 | String[] splits = readSequence.split(","); 86 | StringBuffer sb = new StringBuffer(); 87 | if (splits.length >= 200) { 88 | for (int i = 100; i < splits.length; i++) { 89 | sb.append(splits[i] + ","); 90 | } 91 | readSequence = sb.toString(); 92 | } 93 | 94 | if (!readSequence.contains(entity.getId() + "")) { 95 | readSequence = readSequence + entity.getId() + ","; 96 | } 97 | PreUtils.putStringToDefault(mActivity, "read", readSequence); 98 | TextView tv_title = (TextView) view.findViewById(R.id.tv_title); 99 | tv_title.setTextColor(getResources().getColor(R.color.clicked_tv_textcolor)); 100 | 101 | startActivity(intent); 102 | mActivity.overridePendingTransition(0, 0); 103 | } 104 | }); 105 | lv_news.setOnScrollListener(new AbsListView.OnScrollListener() { 106 | 107 | @Override 108 | public void onScrollStateChanged(AbsListView view, int scrollState) { 109 | 110 | } 111 | 112 | @Override 113 | public void onScroll(AbsListView view, int firstVisibleItem, 114 | int visibleItemCount, int totalItemCount) { 115 | if (lv_news != null && lv_news.getChildCount() > 0) { 116 | boolean enable = (firstVisibleItem == 0) && (view.getChildAt(firstVisibleItem).getTop() == 0); 117 | ((MainActivity) mActivity).setSwipeRefreshEnable(enable); 118 | } 119 | } 120 | }); 121 | return view; 122 | } 123 | 124 | @Override 125 | protected void initData() { 126 | super.initData(); 127 | if (HttpUtils.isNetworkConnected(mActivity)) { 128 | HttpUtils.get(Constant.THEMENEWS + urlId, new TextHttpResponseHandler() { 129 | @Override 130 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { 131 | 132 | } 133 | 134 | @Override 135 | public void onSuccess(int statusCode, Header[] headers, String responseString) { 136 | SQLiteDatabase db = ((MainActivity) mActivity).getCacheDbHelper().getWritableDatabase(); 137 | db.execSQL("replace into CacheList(date,json) values(" + (Constant.BASE_COLUMN + Integer.parseInt(urlId)) + ",' " + responseString + "')"); 138 | db.close(); 139 | parseJson(responseString); 140 | } 141 | }); 142 | } else { 143 | SQLiteDatabase db = ((MainActivity) mActivity).getCacheDbHelper().getReadableDatabase(); 144 | Cursor cursor = db.rawQuery("select * from CacheList where date = " + (Constant.BASE_COLUMN + Integer.parseInt(urlId)), null); 145 | if (cursor.moveToFirst()) { 146 | String json = cursor.getString(cursor.getColumnIndex("json")); 147 | parseJson(json); 148 | } 149 | cursor.close(); 150 | db.close(); 151 | } 152 | 153 | } 154 | 155 | private void parseJson(String responseString) { 156 | Gson gson = new Gson(); 157 | news = gson.fromJson(responseString, News.class); 158 | DisplayImageOptions options = new DisplayImageOptions.Builder() 159 | .cacheInMemory(true) 160 | .cacheOnDisk(true) 161 | .build(); 162 | tv_title.setText(news.getDescription()); 163 | mImageLoader.displayImage(news.getImage(), iv_title, options); 164 | mAdapter = new NewsItemAdapter(mActivity, news.getStories()); 165 | lv_news.setAdapter(mAdapter); 166 | } 167 | 168 | public void updateTheme() { 169 | mAdapter.updateTheme(); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/fragment/MenuFragment.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.fragment; 2 | 3 | import android.content.SharedPreferences; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.preference.Preference; 7 | import android.preference.PreferenceManager; 8 | import android.support.annotation.Nullable; 9 | import android.support.v4.app.Fragment; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.View.OnClickListener; 13 | import android.view.ViewGroup; 14 | import android.widget.AdapterView; 15 | import android.widget.BaseAdapter; 16 | import android.widget.LinearLayout; 17 | import android.widget.ListView; 18 | import android.widget.TextView; 19 | 20 | import com.loopj.android.http.JsonHttpResponseHandler; 21 | 22 | import org.apache.http.Header; 23 | import org.json.JSONArray; 24 | import org.json.JSONException; 25 | import org.json.JSONObject; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import krelve.app.kuaihu.R; 31 | import krelve.app.kuaihu.activity.MainActivity; 32 | import krelve.app.kuaihu.model.NewsListItem; 33 | import krelve.app.kuaihu.util.Constant; 34 | import krelve.app.kuaihu.util.HttpUtils; 35 | import krelve.app.kuaihu.util.PreUtils; 36 | 37 | public class MenuFragment extends BaseFragment implements OnClickListener { 38 | private ListView lv_item; 39 | private TextView tv_download, tv_main, tv_backup, tv_login; 40 | private LinearLayout ll_menu; 41 | // private static String[] ITEMS = { "日常心理学", "用户推荐日报", "电影日报", "不许无聊", 42 | // "设计日报", "大公司日报", "财经日报", "互联网安全", "开始游戏", "音乐日报", "动漫日报", "体育日报" }; 43 | private List items; 44 | private Handler handler = new Handler(); 45 | private boolean isLight; 46 | private NewsTypeAdapter mAdapter; 47 | 48 | 49 | @Override 50 | protected View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 51 | View view = inflater.inflate(R.layout.menu, container, false); 52 | ll_menu = (LinearLayout) view.findViewById(R.id.ll_menu); 53 | tv_login = (TextView) view.findViewById(R.id.tv_login); 54 | tv_backup = (TextView) view.findViewById(R.id.tv_backup); 55 | tv_download = (TextView) view.findViewById(R.id.tv_download); 56 | tv_download.setOnClickListener(this); 57 | tv_main = (TextView) view.findViewById(R.id.tv_main); 58 | tv_main.setOnClickListener(this); 59 | lv_item = (ListView) view.findViewById(R.id.lv_item); 60 | lv_item.setOnItemClickListener(new AdapterView.OnItemClickListener() { 61 | 62 | @Override 63 | public void onItemClick(AdapterView parent, View view, 64 | int position, long id) { 65 | getFragmentManager() 66 | .beginTransaction().setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left) 67 | .replace( 68 | R.id.fl_content, 69 | new NewsFragment(items.get(position) 70 | .getId(), items.get(position).getTitle()), "news").commit(); 71 | ((MainActivity) mActivity).setCurId(items.get(position).getId()); 72 | ((MainActivity) mActivity).closeMenu(); 73 | } 74 | }); 75 | return view; 76 | } 77 | 78 | @Override 79 | protected void initData() { 80 | super.initData(); 81 | isLight = ((MainActivity) mActivity).isLight(); 82 | items = new ArrayList(); 83 | if (HttpUtils.isNetworkConnected(mActivity)) { 84 | HttpUtils.get(Constant.THEMES, new JsonHttpResponseHandler() { 85 | @Override 86 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) { 87 | super.onSuccess(statusCode, headers, response); 88 | String json = response.toString(); 89 | PreUtils.putStringToDefault(mActivity, Constant.THEMES, json); 90 | parseJson(response); 91 | } 92 | }); 93 | } else { 94 | String json = PreUtils.getStringFromDefault(mActivity, Constant.THEMES, ""); 95 | try { 96 | JSONObject jsonObject = new JSONObject(json); 97 | parseJson(jsonObject); 98 | } catch (JSONException e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | 103 | } 104 | 105 | private void parseJson(JSONObject response) { 106 | try { 107 | JSONArray itemsArray = response.getJSONArray("others"); 108 | for (int i = 0; i < itemsArray.length(); i++) { 109 | NewsListItem newsListItem = new NewsListItem(); 110 | JSONObject itemObject = itemsArray.getJSONObject(i); 111 | newsListItem.setTitle(itemObject.getString("name")); 112 | newsListItem.setId(itemObject.getString("id")); 113 | items.add(newsListItem); 114 | } 115 | mAdapter = new NewsTypeAdapter(); 116 | lv_item.setAdapter(mAdapter); 117 | updateTheme(); 118 | } catch (JSONException e) { 119 | e.printStackTrace(); 120 | } 121 | } 122 | 123 | public class NewsTypeAdapter extends BaseAdapter { 124 | 125 | @Override 126 | public int getCount() { 127 | return items.size(); 128 | } 129 | 130 | @Override 131 | public Object getItem(int position) { 132 | return items.get(position); 133 | } 134 | 135 | @Override 136 | public long getItemId(int position) { 137 | return position; 138 | } 139 | 140 | @Override 141 | public View getView(int position, View convertView, ViewGroup parent) { 142 | if (convertView == null) { 143 | convertView = LayoutInflater.from(getActivity()).inflate( 144 | R.layout.menu_item, parent, false); 145 | } 146 | TextView tv_item = (TextView) convertView 147 | .findViewById(R.id.tv_item); 148 | tv_item.setTextColor(getResources().getColor(isLight ? R.color.light_menu_listview_textcolor : R.color.dark_menu_listview_textcolor)); 149 | tv_item.setText(items.get(position).getTitle()); 150 | return convertView; 151 | } 152 | } 153 | 154 | @Override 155 | public void onClick(View v) { 156 | switch (v.getId()) { 157 | case R.id.tv_main: 158 | ((MainActivity) mActivity).loadLatest(); 159 | ((MainActivity) mActivity).closeMenu(); 160 | break; 161 | } 162 | } 163 | 164 | public void updateTheme() { 165 | isLight = ((MainActivity) mActivity).isLight(); 166 | ll_menu.setBackgroundColor(getResources().getColor(isLight ? R.color.light_menu_header : R.color.dark_menu_header)); 167 | tv_login.setTextColor(getResources().getColor(isLight ? R.color.light_menu_header_tv : R.color.dark_menu_header_tv)); 168 | tv_backup.setTextColor(getResources().getColor(isLight ? R.color.light_menu_header_tv : R.color.dark_menu_header_tv)); 169 | tv_download.setTextColor(getResources().getColor(isLight ? R.color.light_menu_header_tv : R.color.dark_menu_header_tv)); 170 | tv_main.setBackgroundColor(getResources().getColor(isLight ? R.color.light_menu_index_background : R.color.dark_menu_index_background)); 171 | lv_item.setBackgroundColor(getResources().getColor(isLight ? R.color.light_menu_listview_background : R.color.dark_menu_listview_background)); 172 | mAdapter.notifyDataSetChanged(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/activity/LatestContentActivity.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.activity; 2 | 3 | import android.annotation.TargetApi; 4 | import android.database.Cursor; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.graphics.Color; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.support.design.widget.AppBarLayout; 10 | import android.support.design.widget.CollapsingToolbarLayout; 11 | import android.support.v7.app.AppCompatActivity; 12 | import android.support.v7.widget.Toolbar; 13 | import android.view.View; 14 | import android.view.ViewTreeObserver; 15 | import android.view.Window; 16 | import android.view.WindowManager; 17 | import android.webkit.WebSettings; 18 | import android.webkit.WebView; 19 | import android.widget.ImageView; 20 | 21 | import com.google.gson.Gson; 22 | import com.loopj.android.http.TextHttpResponseHandler; 23 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 24 | import com.nostra13.universalimageloader.core.ImageLoader; 25 | 26 | import org.apache.http.Header; 27 | 28 | import krelve.app.kuaihu.R; 29 | import krelve.app.kuaihu.db.WebCacheDbHelper; 30 | import krelve.app.kuaihu.model.Content; 31 | import krelve.app.kuaihu.model.StoriesEntity; 32 | import krelve.app.kuaihu.util.Constant; 33 | import krelve.app.kuaihu.util.HttpUtils; 34 | import krelve.app.kuaihu.view.RevealBackgroundView; 35 | 36 | /** 37 | * Created by wwjun.wang on 2015/8/17. 38 | */ 39 | public class LatestContentActivity extends AppCompatActivity implements RevealBackgroundView.OnStateChangeListener { 40 | private WebView mWebView; 41 | private StoriesEntity entity; 42 | private Content content; 43 | private ImageView iv; 44 | private RevealBackgroundView vRevealBackground; 45 | private AppBarLayout mAppBarLayout; 46 | private WebCacheDbHelper dbHelper; 47 | private boolean isLight; 48 | 49 | @Override 50 | protected void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | setContentView(R.layout.latest_content_layout); 53 | dbHelper = new WebCacheDbHelper(this, 1); 54 | isLight = getIntent().getBooleanExtra("isLight", true); 55 | mAppBarLayout = (AppBarLayout) findViewById(R.id.app_bar_layout); 56 | mAppBarLayout.setVisibility(View.INVISIBLE); 57 | vRevealBackground = (RevealBackgroundView) findViewById(R.id.revealBackgroundView); 58 | entity = (StoriesEntity) getIntent().getSerializableExtra("entity"); 59 | iv = (ImageView) findViewById(R.id.iv); 60 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 61 | setSupportActionBar(toolbar); 62 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 63 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 64 | @Override 65 | public void onClick(View v) { 66 | onBackPressed(); 67 | 68 | } 69 | }); 70 | CollapsingToolbarLayout mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout); 71 | mCollapsingToolbarLayout.setTitle(entity.getTitle()); 72 | mCollapsingToolbarLayout.setContentScrimColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 73 | mCollapsingToolbarLayout.setStatusBarScrimColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 74 | mWebView = (WebView) findViewById(R.id.webview); 75 | mWebView.getSettings().setJavaScriptEnabled(true); 76 | mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); 77 | // 开启DOM storage API 功能 78 | mWebView.getSettings().setDomStorageEnabled(true); 79 | // 开启database storage API功能 80 | mWebView.getSettings().setDatabaseEnabled(true); 81 | // 开启Application Cache功能 82 | mWebView.getSettings().setAppCacheEnabled(true); 83 | if (HttpUtils.isNetworkConnected(this)) { 84 | HttpUtils.get(Constant.CONTENT + entity.getId(), new TextHttpResponseHandler() { 85 | @Override 86 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { 87 | 88 | } 89 | 90 | @Override 91 | public void onSuccess(int statusCode, Header[] headers, String responseString) { 92 | SQLiteDatabase db = dbHelper.getWritableDatabase(); 93 | responseString = responseString.replaceAll("'", "''"); 94 | db.execSQL("replace into Cache(newsId,json) values(" + entity.getId() + ",'" + responseString + "')"); 95 | db.close(); 96 | parseJson(responseString); 97 | } 98 | }); 99 | } else { 100 | SQLiteDatabase db = dbHelper.getReadableDatabase(); 101 | Cursor cursor = db.rawQuery("select * from Cache where newsId = " + entity.getId(), null); 102 | if (cursor.moveToFirst()) { 103 | String json = cursor.getString(cursor.getColumnIndex("json")); 104 | parseJson(json); 105 | } 106 | cursor.close(); 107 | db.close(); 108 | } 109 | setupRevealBackground(savedInstanceState); 110 | setStatusBarColor(getResources().getColor(isLight ? R.color.light_toolbar : R.color.dark_toolbar)); 111 | } 112 | 113 | private void parseJson(String responseString) { 114 | Gson gson = new Gson(); 115 | content = gson.fromJson(responseString, Content.class); 116 | final ImageLoader imageloader = ImageLoader.getInstance(); 117 | DisplayImageOptions options = new DisplayImageOptions.Builder() 118 | .cacheInMemory(true) 119 | .cacheOnDisk(true) 120 | .build(); 121 | imageloader.displayImage(content.getImage(), iv, options); 122 | String css = ""; 123 | String html = "" + css + "" + content.getBody() + ""; 124 | html = html.replace("
", ""); 125 | mWebView.loadDataWithBaseURL("x-data://base", html, "text/html", "UTF-8", null); 126 | } 127 | 128 | 129 | private void setupRevealBackground(Bundle savedInstanceState) { 130 | vRevealBackground.setOnStateChangeListener(this); 131 | if (savedInstanceState == null) { 132 | final int[] startingLocation = getIntent().getIntArrayExtra(Constant.START_LOCATION); 133 | vRevealBackground.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 134 | @Override 135 | public boolean onPreDraw() { 136 | vRevealBackground.getViewTreeObserver().removeOnPreDrawListener(this); 137 | vRevealBackground.startFromLocation(startingLocation); 138 | return true; 139 | } 140 | }); 141 | } else { 142 | vRevealBackground.setToFinishedFrame(); 143 | } 144 | } 145 | 146 | @Override 147 | public void onStateChange(int state) { 148 | if (RevealBackgroundView.STATE_FINISHED == state) { 149 | mAppBarLayout.setVisibility(View.VISIBLE); 150 | setStatusBarColor(Color.TRANSPARENT); 151 | } 152 | } 153 | 154 | @Override 155 | public void onBackPressed() { 156 | finish(); 157 | overridePendingTransition(0, R.anim.slide_out_to_left_from_right); 158 | } 159 | 160 | @TargetApi(21) 161 | private void setStatusBarColor(int statusBarColor) { 162 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 163 | // If both system bars are black, we can remove these from our layout, 164 | // removing or shrinking the SurfaceFlinger overlay required for our views. 165 | Window window = this.getWindow(); 166 | if (statusBarColor == Color.BLACK && window.getNavigationBarColor() == Color.BLACK) { 167 | window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 168 | } else { 169 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 170 | } 171 | window.setStatusBarColor(statusBarColor); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/view/Kanner.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.view; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.support.v4.view.PagerAdapter; 6 | import android.support.v4.view.ViewPager; 7 | import android.support.v4.view.ViewPager.OnPageChangeListener; 8 | import android.util.AttributeSet; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.View.OnClickListener; 12 | import android.view.ViewGroup; 13 | import android.widget.FrameLayout; 14 | import android.widget.ImageView; 15 | import android.widget.ImageView.ScaleType; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | 19 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 20 | import com.nostra13.universalimageloader.core.ImageLoader; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import krelve.app.kuaihu.Kpplication; 26 | import krelve.app.kuaihu.R; 27 | import krelve.app.kuaihu.model.Latest; 28 | 29 | public class Kanner extends FrameLayout implements OnClickListener { 30 | private List topStoriesEntities; 31 | private ImageLoader mImageLoader; 32 | private DisplayImageOptions options; 33 | private List views; 34 | private Context context; 35 | private ViewPager vp; 36 | private boolean isAutoPlay; 37 | private int currentItem; 38 | private int delayTime; 39 | private LinearLayout ll_dot; 40 | private List iv_dots; 41 | private Handler handler = new Handler(); 42 | private OnItemClickListener mItemClickListener; 43 | 44 | public Kanner(Context context, AttributeSet attrs, int defStyle) { 45 | super(context, attrs, defStyle); 46 | mImageLoader = ImageLoader.getInstance(); 47 | options = new DisplayImageOptions.Builder() 48 | .cacheInMemory(true) 49 | .cacheOnDisk(true) 50 | .build(); 51 | this.context = context; 52 | this.topStoriesEntities = new ArrayList<>(); 53 | initView(); 54 | } 55 | 56 | private void initView() { 57 | views = new ArrayList(); 58 | iv_dots = new ArrayList(); 59 | delayTime = 2000; 60 | } 61 | 62 | public Kanner(Context context, AttributeSet attrs) { 63 | this(context, attrs, 0); 64 | } 65 | 66 | public Kanner(Context context) { 67 | this(context, null); 68 | } 69 | 70 | public void setTopEntities(List topEntities) { 71 | this.topStoriesEntities = topEntities; 72 | reset(); 73 | } 74 | 75 | private void reset() { 76 | views.clear(); 77 | initUI(); 78 | } 79 | 80 | private void initUI() { 81 | View view = LayoutInflater.from(context).inflate( 82 | R.layout.kanner_layout, this, true); 83 | vp = (ViewPager) view.findViewById(R.id.vp); 84 | ll_dot = (LinearLayout) view.findViewById(R.id.ll_dot); 85 | ll_dot.removeAllViews(); 86 | 87 | int len = topStoriesEntities.size(); 88 | for (int i = 0; i < len; i++) { 89 | ImageView iv_dot = new ImageView(context); 90 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 91 | LinearLayout.LayoutParams.WRAP_CONTENT, 92 | LinearLayout.LayoutParams.WRAP_CONTENT); 93 | params.leftMargin = 5; 94 | params.rightMargin = 5; 95 | ll_dot.addView(iv_dot, params); 96 | iv_dots.add(iv_dot); 97 | } 98 | 99 | for (int i = 0; i <= len + 1; i++) { 100 | View fm = LayoutInflater.from(context).inflate( 101 | R.layout.kanner_content_layout, null); 102 | ImageView iv = (ImageView) fm.findViewById(R.id.iv_title); 103 | TextView tv_title = (TextView) fm.findViewById(R.id.tv_title); 104 | iv.setScaleType(ScaleType.CENTER_CROP); 105 | // iv.setBackgroundResource(R.drawable.loading1); 106 | if (i == 0) { 107 | mImageLoader.displayImage(topStoriesEntities.get(len - 1).getImage(), iv, options); 108 | tv_title.setText(topStoriesEntities.get(len - 1).getTitle()); 109 | } else if (i == len + 1) { 110 | mImageLoader.displayImage(topStoriesEntities.get(0).getImage(), iv, options); 111 | tv_title.setText(topStoriesEntities.get(0).getTitle()); 112 | } else { 113 | mImageLoader.displayImage(topStoriesEntities.get(i - 1).getImage(), iv, options); 114 | tv_title.setText(topStoriesEntities.get(i - 1).getTitle()); 115 | } 116 | fm.setOnClickListener(this); 117 | views.add(fm); 118 | } 119 | vp.setAdapter(new MyPagerAdapter()); 120 | vp.setFocusable(true); 121 | vp.setCurrentItem(1); 122 | currentItem = 1; 123 | vp.addOnPageChangeListener(new MyOnPageChangeListener()); 124 | startPlay(); 125 | } 126 | 127 | private void startPlay() { 128 | isAutoPlay = true; 129 | handler.postDelayed(task, 3000); 130 | } 131 | 132 | 133 | private final Runnable task = new Runnable() { 134 | 135 | @Override 136 | public void run() { 137 | if (isAutoPlay) { 138 | currentItem = currentItem % (topStoriesEntities.size() + 1) + 1; 139 | if (currentItem == 1) { 140 | vp.setCurrentItem(currentItem, false); 141 | handler.post(task); 142 | } else { 143 | vp.setCurrentItem(currentItem); 144 | handler.postDelayed(task, 5000); 145 | } 146 | } else { 147 | handler.postDelayed(task, 5000); 148 | } 149 | } 150 | }; 151 | 152 | class MyPagerAdapter extends PagerAdapter { 153 | 154 | @Override 155 | public int getCount() { 156 | return views.size(); 157 | } 158 | 159 | @Override 160 | public boolean isViewFromObject(View arg0, Object arg1) { 161 | return arg0 == arg1; 162 | } 163 | 164 | @Override 165 | public Object instantiateItem(ViewGroup container, int position) { 166 | container.addView(views.get(position)); 167 | return views.get(position); 168 | } 169 | 170 | @Override 171 | public void destroyItem(ViewGroup container, int position, Object object) { 172 | container.removeView((View) object); 173 | } 174 | 175 | } 176 | 177 | class MyOnPageChangeListener implements OnPageChangeListener { 178 | 179 | @Override 180 | public void onPageScrollStateChanged(int arg0) { 181 | switch (arg0) { 182 | case 1: 183 | isAutoPlay = false; 184 | break; 185 | case 2: 186 | isAutoPlay = true; 187 | break; 188 | case 0: 189 | if (vp.getCurrentItem() == 0) { 190 | vp.setCurrentItem(topStoriesEntities.size(), false); 191 | } else if (vp.getCurrentItem() == topStoriesEntities.size() + 1) { 192 | vp.setCurrentItem(1, false); 193 | } 194 | currentItem = vp.getCurrentItem(); 195 | isAutoPlay = true; 196 | break; 197 | } 198 | } 199 | 200 | @Override 201 | public void onPageScrolled(int arg0, float arg1, int arg2) { 202 | } 203 | 204 | @Override 205 | public void onPageSelected(int arg0) { 206 | for (int i = 0; i < iv_dots.size(); i++) { 207 | if (i == arg0 - 1) { 208 | iv_dots.get(i).setImageResource(R.drawable.dot_focus); 209 | } else { 210 | iv_dots.get(i).setImageResource(R.drawable.dot_blur); 211 | } 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | 219 | public void setOnItemClickListener(OnItemClickListener mItemClickListener) { 220 | this.mItemClickListener = mItemClickListener; 221 | } 222 | 223 | public interface OnItemClickListener { 224 | public void click(View v, Latest.TopStoriesEntity entity); 225 | } 226 | 227 | @Override 228 | public void onClick(View v) { 229 | if (mItemClickListener != null) { 230 | Latest.TopStoriesEntity entity = topStoriesEntities.get(vp.getCurrentItem() - 1); 231 | mItemClickListener.click(v, entity); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /app/src/main/java/krelve/app/kuaihu/fragment/MainFragment.java: -------------------------------------------------------------------------------- 1 | package krelve.app.kuaihu.fragment; 2 | 3 | import android.content.Intent; 4 | import android.database.Cursor; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.support.design.widget.Snackbar; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.AbsListView; 13 | import android.widget.AdapterView; 14 | import android.widget.ListView; 15 | import android.widget.TextView; 16 | 17 | import com.google.gson.Gson; 18 | import com.loopj.android.http.TextHttpResponseHandler; 19 | 20 | import org.apache.http.Header; 21 | 22 | import java.util.List; 23 | 24 | import krelve.app.kuaihu.R; 25 | import krelve.app.kuaihu.activity.LatestContentActivity; 26 | import krelve.app.kuaihu.activity.MainActivity; 27 | import krelve.app.kuaihu.adapter.MainNewsItemAdapter; 28 | import krelve.app.kuaihu.db.CacheDbHelper; 29 | import krelve.app.kuaihu.model.Before; 30 | import krelve.app.kuaihu.model.Latest; 31 | import krelve.app.kuaihu.model.StoriesEntity; 32 | import krelve.app.kuaihu.util.Constant; 33 | import krelve.app.kuaihu.util.HttpUtils; 34 | import krelve.app.kuaihu.util.PreUtils; 35 | import krelve.app.kuaihu.view.Kanner; 36 | 37 | /** 38 | * Created by wwjun.wang on 2015/8/12. 39 | */ 40 | public class MainFragment extends BaseFragment { 41 | private ListView lv_news; 42 | private MainNewsItemAdapter mAdapter; 43 | private Latest latest; 44 | private Before before; 45 | private Kanner kanner; 46 | private String date; 47 | private boolean isLoading = false; 48 | private Handler handler = new Handler(); 49 | 50 | @Override 51 | protected View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 52 | ((MainActivity) mActivity).setToolbarTitle("今日热闻"); 53 | View view = inflater.inflate(R.layout.main_news_layout, container, false); 54 | lv_news = (ListView) view.findViewById(R.id.lv_news); 55 | View header = inflater.inflate(R.layout.kanner, lv_news, false); 56 | kanner = (Kanner) header.findViewById(R.id.kanner); 57 | kanner.setOnItemClickListener(new Kanner.OnItemClickListener() { 58 | @Override 59 | public void click(View v, Latest.TopStoriesEntity entity) { 60 | int[] startingLocation = new int[2]; 61 | v.getLocationOnScreen(startingLocation); 62 | startingLocation[0] += v.getWidth() / 2; 63 | StoriesEntity storiesEntity = new StoriesEntity(); 64 | storiesEntity.setId(entity.getId()); 65 | storiesEntity.setTitle(entity.getTitle()); 66 | Intent intent = new Intent(mActivity, LatestContentActivity.class); 67 | intent.putExtra(Constant.START_LOCATION, startingLocation); 68 | intent.putExtra("entity", storiesEntity); 69 | intent.putExtra("isLight", ((MainActivity) mActivity).isLight()); 70 | startActivity(intent); 71 | mActivity.overridePendingTransition(0, 0); 72 | } 73 | }); 74 | lv_news.addHeaderView(header); 75 | mAdapter = new MainNewsItemAdapter(mActivity); 76 | lv_news.setAdapter(mAdapter); 77 | lv_news.setOnScrollListener(new AbsListView.OnScrollListener() { 78 | @Override 79 | public void onScrollStateChanged(AbsListView view, int scrollState) { 80 | 81 | } 82 | 83 | @Override 84 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 85 | if (lv_news != null && lv_news.getChildCount() > 0) { 86 | boolean enable = (firstVisibleItem == 0) && (view.getChildAt(firstVisibleItem).getTop() == 0); 87 | ((MainActivity) mActivity).setSwipeRefreshEnable(enable); 88 | 89 | if (firstVisibleItem + visibleItemCount == totalItemCount && !isLoading) { 90 | loadMore(Constant.BEFORE + date); 91 | } 92 | } 93 | 94 | } 95 | }); 96 | lv_news.setOnItemClickListener(new AdapterView.OnItemClickListener() { 97 | @Override 98 | public void onItemClick(AdapterView parent, View view, int position, long id) { 99 | int[] startingLocation = new int[2]; 100 | view.getLocationOnScreen(startingLocation); 101 | startingLocation[0] += view.getWidth() / 2; 102 | StoriesEntity entity = (StoriesEntity) parent.getAdapter().getItem(position); 103 | Intent intent = new Intent(mActivity, LatestContentActivity.class); 104 | intent.putExtra(Constant.START_LOCATION, startingLocation); 105 | intent.putExtra("entity", entity); 106 | intent.putExtra("isLight", ((MainActivity) mActivity).isLight()); 107 | String readSequence = PreUtils.getStringFromDefault(mActivity, "read", ""); 108 | String[] splits = readSequence.split(","); 109 | StringBuffer sb = new StringBuffer(); 110 | if (splits.length >= 200) { 111 | for (int i = 100; i < splits.length; i++) { 112 | sb.append(splits[i] + ","); 113 | } 114 | readSequence = sb.toString(); 115 | } 116 | 117 | if (!readSequence.contains(entity.getId() + "")) { 118 | readSequence = readSequence + entity.getId() + ","; 119 | } 120 | PreUtils.putStringToDefault(mActivity, "read", readSequence); 121 | TextView tv_title = (TextView) view.findViewById(R.id.tv_title); 122 | tv_title.setTextColor(getResources().getColor(R.color.clicked_tv_textcolor)); 123 | startActivity(intent); 124 | mActivity.overridePendingTransition(0, 0); 125 | } 126 | }); 127 | return view; 128 | } 129 | 130 | private void loadFirst() { 131 | isLoading = true; 132 | if (HttpUtils.isNetworkConnected(mActivity)) { 133 | HttpUtils.get(Constant.LATESTNEWS, new TextHttpResponseHandler() { 134 | @Override 135 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { 136 | } 137 | 138 | @Override 139 | public void onSuccess(int statusCode, Header[] headers, String responseString) { 140 | SQLiteDatabase db = ((MainActivity) mActivity).getCacheDbHelper().getWritableDatabase(); 141 | db.execSQL("replace into CacheList(date,json) values(" + Constant.LATEST_COLUMN + ",' " + responseString + "')"); 142 | db.close(); 143 | parseLatestJson(responseString); 144 | } 145 | 146 | }); 147 | } else { 148 | SQLiteDatabase db = ((MainActivity) mActivity).getCacheDbHelper().getReadableDatabase(); 149 | Cursor cursor = db.rawQuery("select * from CacheList where date = " + Constant.LATEST_COLUMN, null); 150 | if (cursor.moveToFirst()) { 151 | String json = cursor.getString(cursor.getColumnIndex("json")); 152 | parseLatestJson(json); 153 | } else { 154 | isLoading = false; 155 | } 156 | cursor.close(); 157 | db.close(); 158 | } 159 | 160 | } 161 | 162 | private void parseLatestJson(String responseString) { 163 | Gson gson = new Gson(); 164 | latest = gson.fromJson(responseString, Latest.class); 165 | date = latest.getDate(); 166 | kanner.setTopEntities(latest.getTop_stories()); 167 | handler.post(new Runnable() { 168 | @Override 169 | public void run() { 170 | List storiesEntities = latest.getStories(); 171 | StoriesEntity topic = new StoriesEntity(); 172 | topic.setType(Constant.TOPIC); 173 | topic.setTitle("今日热闻"); 174 | storiesEntities.add(0, topic); 175 | mAdapter.addList(storiesEntities); 176 | isLoading = false; 177 | } 178 | }); 179 | } 180 | 181 | private void loadMore(final String url) { 182 | isLoading = true; 183 | if (HttpUtils.isNetworkConnected(mActivity)) { 184 | HttpUtils.get(url, new TextHttpResponseHandler() { 185 | @Override 186 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { 187 | } 188 | 189 | @Override 190 | public void onSuccess(int statusCode, Header[] headers, String responseString) { 191 | // PreUtils.putStringTo(Constant.CACHE, mActivity, url, responseString); 192 | SQLiteDatabase db = ((MainActivity) mActivity).getCacheDbHelper().getWritableDatabase(); 193 | db.execSQL("replace into CacheList(date,json) values(" + date + ",' " + responseString + "')"); 194 | db.close(); 195 | parseBeforeJson(responseString); 196 | 197 | } 198 | 199 | }); 200 | } else { 201 | SQLiteDatabase db = ((MainActivity) mActivity).getCacheDbHelper().getReadableDatabase(); 202 | Cursor cursor = db.rawQuery("select * from CacheList where date = " + date, null); 203 | if (cursor.moveToFirst()) { 204 | String json = cursor.getString(cursor.getColumnIndex("json")); 205 | parseBeforeJson(json); 206 | } else { 207 | db.delete("CacheList", "date < " + date, null); 208 | isLoading = false; 209 | Snackbar sb = Snackbar.make(lv_news, "没有更多的离线内容了~", Snackbar.LENGTH_SHORT); 210 | sb.getView().setBackgroundColor(getResources().getColor(((MainActivity) mActivity).isLight() ? android.R.color.holo_blue_dark : android.R.color.black)); 211 | sb.show(); 212 | } 213 | cursor.close(); 214 | db.close(); 215 | } 216 | } 217 | 218 | private void parseBeforeJson(String responseString) { 219 | Gson gson = new Gson(); 220 | before = gson.fromJson(responseString, Before.class); 221 | if (before == null) { 222 | isLoading = false; 223 | return; 224 | } 225 | date = before.getDate(); 226 | handler.post(new Runnable() { 227 | @Override 228 | public void run() { 229 | List storiesEntities = before.getStories(); 230 | StoriesEntity topic = new StoriesEntity(); 231 | topic.setType(Constant.TOPIC); 232 | topic.setTitle(convertDate(date)); 233 | storiesEntities.add(0, topic); 234 | mAdapter.addList(storiesEntities); 235 | isLoading = false; 236 | } 237 | }); 238 | } 239 | 240 | @Override 241 | protected void initData() { 242 | super.initData(); 243 | loadFirst(); 244 | } 245 | 246 | private String convertDate(String date) { 247 | String result = date.substring(0, 4); 248 | result += "年"; 249 | result += date.substring(4, 6); 250 | result += "月"; 251 | result += date.substring(6, 8); 252 | result += "日"; 253 | return result; 254 | } 255 | 256 | public void updateTheme() { 257 | mAdapter.updateTheme(); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /app/src/main/assets/css/news.css: -------------------------------------------------------------------------------- 1 | article, 2 | aside, 3 | details, 4 | figcaption, 5 | figure, 6 | footer, 7 | header, 8 | hgroup, 9 | main, 10 | nav, 11 | section, 12 | summary { 13 | display: block; 14 | } 15 | audio, 16 | canvas, 17 | video { 18 | display: inline-block; 19 | } 20 | audio:not([controls]) { 21 | display: none; 22 | height: 0; 23 | } 24 | html { 25 | font-family: sans-serif; 26 | -webkit-text-size-adjust: 100%; 27 | } 28 | body { 29 | font-family: 'Helvetica Neue', Helvetica, Arial, Sans-serif; 30 | background: #fff; 31 | padding-top: 0; 32 | margin: 0; 33 | } 34 | a:focus { 35 | outline: thin dotted; 36 | } 37 | a:active, 38 | a:hover { 39 | outline: 0; 40 | } 41 | h1 { 42 | margin: .67em 0; 43 | } 44 | h1, 45 | h2, 46 | h3, 47 | h4, 48 | h5, 49 | h6 { 50 | font-size: 16px; 51 | } 52 | abbr[title] { 53 | border-bottom: 1px dotted; 54 | } 55 | hr { 56 | box-sizing: content-box; 57 | height: 0; 58 | } 59 | mark { 60 | background: #ff0; 61 | color: #000; 62 | } 63 | code, 64 | kbd, 65 | pre, 66 | samp { 67 | font-family: monospace,serif; 68 | font-size: 1em; 69 | } 70 | pre { 71 | white-space: pre-wrap; 72 | } 73 | q { 74 | quotes: \201C\201D\2018\2019; 75 | } 76 | small { 77 | font-size: 80%; 78 | } 79 | sub, 80 | sup { 81 | font-size: 75%; 82 | line-height: 0; 83 | position: relative; 84 | vertical-align: baseline; 85 | } 86 | sup { 87 | top: -0.5em; 88 | } 89 | sub { 90 | bottom: -0.25em; 91 | } 92 | img { 93 | border: 0; 94 | vertical-align: middle; 95 | color: transparent; 96 | font-size: 0; 97 | } 98 | svg:not(:root) { 99 | overflow: hidden; 100 | } 101 | figure { 102 | margin: 0; 103 | } 104 | fieldset { 105 | border: 1px solid silver; 106 | margin: 0 2px; 107 | padding: .35em .625em .75em; 108 | } 109 | legend { 110 | border: 0; 111 | padding: 0; 112 | } 113 | table { 114 | border-collapse: collapse; 115 | border-spacing: 0; 116 | overflow: hidden; 117 | } 118 | a { 119 | text-decoration: none; 120 | } 121 | blockquote { 122 | border-left: 3px solid #D0E5F2; 123 | font-style: normal; 124 | display: block; 125 | line-height: 1.4em; 126 | vertical-align: baseline; 127 | font-size: 100%; 128 | margin: .5em 0; 129 | padding: 0 0 0 .5em; 130 | } 131 | ul, 132 | ol { 133 | padding-left: 20px; 134 | } 135 | .main-wrap { 136 | max-width: 100%; 137 | min-width: 300px; 138 | margin: 0 auto; 139 | } 140 | .content-wrap { 141 | overflow: hidden; 142 | background-color: #f9f9f9; 143 | } 144 | .content-wrap a { 145 | word-break: break-all; 146 | } 147 | .headline { 148 | border-bottom: 4px solid #f6f6f6; 149 | } 150 | .headline-title.onlyheading { 151 | margin: 20px 0; 152 | } 153 | .headline img { 154 | max-width: 100%; 155 | vertical-align: top; 156 | } 157 | .headline-background-link { 158 | line-height: 2em; 159 | position: relative; 160 | display: block; 161 | padding: 20px 45px 20px 20px !important; 162 | } 163 | .icon-arrow-right { 164 | position: absolute; 165 | top: 50%; 166 | right: 20px; 167 | background-image: url(http://static.daily.zhihu.com/img/share-icons.png); 168 | background-repeat: no-repeat; 169 | display: inline-block; 170 | vertical-align: middle; 171 | background-position: -70px -20px; 172 | width: 10px; 173 | height: 15px; 174 | margin-top: -7.5px; 175 | } 176 | .headline-background .heading { 177 | color: #999; 178 | font-size: 15px!important; 179 | margin-bottom: 8px; 180 | line-height: 1em; 181 | } 182 | .headline-background .heading-content { 183 | color: #444; 184 | font-size: 17px!important; 185 | line-height: 1.2em; 186 | } 187 | .headline-title { 188 | line-height: 1.2em; 189 | color: #000; 190 | font-size: 22px; 191 | margin: 20px 0 10px; 192 | padding: 0 20px!important; 193 | font-weight: bold; 194 | } 195 | .meta { 196 | white-space: nowrap; 197 | text-overflow: ellipsis; 198 | overflow: hidden; 199 | font-size: 16px; 200 | color: #b8b8b8; 201 | } 202 | .meta .source-icon { 203 | width: 20px; 204 | height: 20px; 205 | margin-right: 4px; 206 | } 207 | .meta .time { 208 | float: right; 209 | margin-top: 2px; 210 | } 211 | .content { 212 | color: #444; 213 | line-height: 1.6em; 214 | font-size: 17px; 215 | margin: 10px 0 20px; 216 | } 217 | .content img { 218 | max-width: 100%; 219 | display: block; 220 | margin: 10px 0; 221 | } 222 | .content img[src*="zhihu.com/equation"] { 223 | display: inline-block; 224 | margin: 0 3px; 225 | } 226 | .content a { 227 | color: #259; 228 | } 229 | .content a:hover { 230 | text-decoration: underline; 231 | } 232 | .view-more { 233 | margin-bottom: 25px; 234 | text-align: center; 235 | } 236 | .view-more a { 237 | font-size: 16px; 238 | display: inline-block; 239 | width: 125px; 240 | height: 30px; 241 | line-height: 30px; 242 | background: #f0f0f0; 243 | color: #B8B8B8; 244 | } 245 | .question { 246 | overflow: hidden; 247 | padding: 0 20px!important; 248 | } 249 | .question + .question { 250 | border-top: 5px solid #f6f6f6; 251 | } 252 | .question-title { 253 | line-height: 1.4em; 254 | color: #000; 255 | font-weight: 700; 256 | font-size: 18px; 257 | margin: 20px 0; 258 | } 259 | .meta .author { 260 | color: #444; 261 | font-weight: 700; 262 | } 263 | .answer + .answer { 264 | border-top: 2px solid #f6f6f6; 265 | padding-top: 20px; 266 | } 267 | .footer { 268 | text-align: center; 269 | color: #b8b8b8; 270 | font-size: 13px; 271 | padding: 20px 0; 272 | } 273 | .footer a { 274 | color: #b8b8b8; 275 | } 276 | .question .view-more a { 277 | width: 100%; 278 | display: block; 279 | } 280 | .hot-comment { 281 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 282 | } 283 | .comment-label { 284 | font-size: 16px; 285 | color: #333; 286 | line-height: 1.5em; 287 | font-weight: 700; 288 | border-top: 1px solid #eee; 289 | border-bottom: 1px solid #eee; 290 | margin: 0; 291 | padding: 9px 20px; 292 | } 293 | .comment-list { 294 | margin-bottom: 20px; 295 | } 296 | .comment-item { 297 | font-size: 15px; 298 | color: #666; 299 | border-bottom: 1px solid #eee; 300 | padding: 15px 20px; 301 | } 302 | .comment-meta { 303 | position: relative; 304 | margin-bottom: 10px; 305 | } 306 | .comment-meta .author { 307 | vertical-align: middle; 308 | color: #444; 309 | } 310 | .comment-meta .vote { 311 | position: absolute; 312 | color: #b8b8b8; 313 | font-size: 12px; 314 | right: 0; 315 | } 316 | .night .comment-label { 317 | color: #b8b8b8; 318 | border-top: 1px solid #303030; 319 | border-bottom: 1px solid #303030; 320 | } 321 | .night .comment-item { 322 | color: #7f7f7f; 323 | border-bottom: 1px solid #303030; 324 | } 325 | .icon-vote, 326 | .icon-voted { 327 | background-repeat: no-repeat; 328 | display: inline-block; 329 | vertical-align: 0; 330 | width: 11px; 331 | height: 12px; 332 | margin-right: 4px; 333 | background-image: url(http://static.daily.zhihu.com/img/app/Comment_Vote.png) !important; 334 | } 335 | .icon-voted { 336 | background-image: url(http://static.daily.zhihu.com/img/app/Comment_Voted.png) !important; 337 | } 338 | .night .icon-vote { 339 | background-image: url(http://static.daily.zhihu.com/img/app/Dark_Comment_Vote.png) !important; 340 | } 341 | .img-wrap .headline-title { 342 | bottom: 5px; 343 | } 344 | .img-wrap .img-source { 345 | right: 10px!important; 346 | font-size: 9px; 347 | } 348 | .global-header { 349 | position: static; 350 | } 351 | .button { 352 | width: 60px; 353 | } 354 | .button i { 355 | margin-right: 0; 356 | } 357 | .headline .img-place-holder { 358 | height: 200px; 359 | } 360 | .from-column { 361 | width: 280px; 362 | line-height: 30px; 363 | height: 30px; 364 | padding-left: 90px; 365 | color: #2aacec; 366 | background-image: url(http://static.daily.zhihu.com/img/News_Column_Entrance.png); 367 | box-sizing: border-box; 368 | margin: 0 20px 20px; 369 | } 370 | .from-column:active { 371 | background-image: url(http://static.daily.zhihu.com/img/News_Column_Entrance_Highlight.png); 372 | } 373 | .night .headline { 374 | border-bottom: 4px solid #303030; 375 | } 376 | .night img { 377 | -webkit-mask-image: -webkit-gradient(linear, 0 0, 0 100%, from(rgba(0, 0, 0, 0.7)), to(rgba(0, 0, 0, 0.7))); 378 | } 379 | body.night, 380 | .night .content-wrap { 381 | background: #343434; 382 | } 383 | .night .answer + .answer { 384 | border-top: 2px solid #303030; 385 | } 386 | .night .question + .question { 387 | border-top: 4px solid #303030; 388 | } 389 | .night .view-more a { 390 | background: #292929; 391 | color: #666; 392 | } 393 | .night .icon-arrow-right { 394 | background-image: url(http://static.daily.zhihu.com/img/share-icons.png); 395 | background-repeat: no-repeat; 396 | display: inline-block; 397 | vertical-align: middle; 398 | background-position: -70px -35px; 399 | width: 10px; 400 | height: 15px; 401 | } 402 | .night blockquote, 403 | .night sup { 404 | border-left: 3px solid #666; 405 | } 406 | .night .content a { 407 | color: #698ebf; 408 | } 409 | .night .from-column { 410 | color: #2b82ac; 411 | background-image: url(http://static.daily.zhihu.com/img/Dark_News_Column_Entrance.png); 412 | } 413 | .night .from-column:active { 414 | background-image: url(http://static.daily.zhihu.com/img/Dark_News_Column_Entrance_Highlight.png); 415 | } 416 | .large .question-title { 417 | font-size: 24px; 418 | } 419 | .large .meta { 420 | font-size: 18px; 421 | } 422 | .large .content { 423 | font-size: 20px; 424 | } 425 | .large blockquote, 426 | .large sup { 427 | line-height: 1.6; 428 | } 429 | .meta .meta-item { 430 | -o-text-overflow: ellipsis; 431 | width: 39%; 432 | overflow: hidden; 433 | white-space: nowrap; 434 | text-overflow: ellipsis; 435 | display: inline-block; 436 | color: #929292; 437 | margin-right: 7px; 438 | } 439 | .headline .meta { 440 | white-space: nowrap; 441 | text-overflow: ellipsis; 442 | overflow: hidden; 443 | font-size: 11px; 444 | color: #b8b8b8; 445 | margin: 15px 0; 446 | padding: 0 20px; 447 | } 448 | .headline .meta a, 449 | .headline .meta a:hover { 450 | padding-left: 1em; 451 | margin-top: 2px; 452 | float: right; 453 | font-size: 11px; 454 | color: #0066cf; 455 | text-decoration: none; 456 | } 457 | .highlight { 458 | overflow: auto; 459 | word-wrap: normal; 460 | background: #fff; 461 | } 462 | .highlight pre { 463 | white-space: pre; 464 | } 465 | .highlight .hll { 466 | background-color: #ffc; 467 | } 468 | .highlight .err { 469 | color: #a61717; 470 | background-color: #e3d2d2; 471 | } 472 | .highlight .cp { 473 | color: #999; 474 | font-weight: 700; 475 | } 476 | .highlight .cs { 477 | color: #999; 478 | font-weight: 700; 479 | font-style: italic; 480 | } 481 | .highlight .gd { 482 | color: #000; 483 | background-color: #fdd; 484 | } 485 | .highlight .gi { 486 | color: #000; 487 | background-color: #dfd; 488 | } 489 | .highlight .gu { 490 | color: #aaa; 491 | } 492 | .highlight .ni { 493 | color: purple; 494 | } 495 | .highlight .nt { 496 | color: navy; 497 | } 498 | .highlight .w { 499 | color: #bbb; 500 | } 501 | .highlight .sr { 502 | color: olive; 503 | } 504 | [hidden], 505 | .button span { 506 | display: none; 507 | } 508 | b, 509 | strong, 510 | .highlight .k, 511 | .highlight .o, 512 | .highlight .gs, 513 | .highlight .kc, 514 | .highlight .kd, 515 | .highlight .kn, 516 | .highlight .kp, 517 | .highlight .kr, 518 | .highlight .ow { 519 | font-weight: 700; 520 | } 521 | dfn, 522 | .highlight .ge { 523 | font-style: italic; 524 | } 525 | .meta span, 526 | .meta .source { 527 | vertical-align: middle; 528 | } 529 | .meta .avatar, 530 | .comment-meta .avatar { 531 | width: 20px; 532 | height: 20px; 533 | border-radius: 2px; 534 | margin-right: 5px; 535 | } 536 | .meta .bio, 537 | .highlight .gh, 538 | .highlight .bp { 539 | color: #999; 540 | } 541 | .night .comment-meta .author, 542 | .night .content, 543 | .night .meta .author, 544 | .highlight .go { 545 | color: #888; 546 | } 547 | .night .headline-title, 548 | .night .headline-background .heading-content, 549 | .night .question-title { 550 | color: #B8B8B8; 551 | } 552 | .highlight .c, 553 | .highlight .cm, 554 | .highlight .c1 { 555 | color: #998; 556 | font-style: italic; 557 | } 558 | .highlight .gr, 559 | .highlight .gt { 560 | color: #a00; 561 | } 562 | .highlight .gp, 563 | .highlight .nn { 564 | color: #555; 565 | } 566 | .highlight .kt, 567 | .highlight .nc { 568 | color: #458; 569 | font-weight: 700; 570 | } 571 | .highlight .m, 572 | .highlight .mf, 573 | .highlight .mh, 574 | .highlight .mi, 575 | .highlight .mo, 576 | .highlight .il { 577 | color: #099; 578 | } 579 | .highlight .s, 580 | .highlight .sb, 581 | .highlight .sc, 582 | .highlight .sd, 583 | .highlight .s2, 584 | .highlight .se, 585 | .highlight .sh, 586 | .highlight .si, 587 | .highlight .sx, 588 | .highlight .s1, 589 | .highlight .ss { 590 | color: #d32; 591 | } 592 | .highlight .na, 593 | .highlight .nb, 594 | .highlight .no, 595 | .highlight .nv, 596 | .highlight .vc, 597 | .highlight .vg, 598 | .highlight .vi { 599 | color: teal; 600 | } 601 | .highlight .ne, 602 | .highlight .nf { 603 | color: #900; 604 | font-weight: 700; 605 | } 606 | .answer h1, 607 | .answer h2, 608 | .answer h3, 609 | .answer h4, 610 | .answer h5 { 611 | font-size: 19px; 612 | } 613 | @media only screen and (-webkit-min-device-pixel-ratio2), only screen and (min-device-pixel-ratio2) { 614 | .icon-arrow-right { 615 | background-image: url(http://static.daily.zhihu.com/img/share-icons@2x.png); 616 | -webkit-background-size: 82px 55px; 617 | background-size: 82px 55px; 618 | } 619 | .icon-vote, 620 | .icon-voted { 621 | background-image: url(http://static.daily.zhihu.com/img/app/Comment_Vote@2x.png) !important; 622 | background-size: 11px 12px; 623 | } 624 | .icon-voted { 625 | background-image: url(http://static.daily.zhihu.com/img/app/Comment_Voted@2x.png) !important; 626 | } 627 | .night .icon-vote { 628 | background-image: url(http://static.daily.zhihu.com/img/app/Dark_Comment_Vote@2x.png) !important; 629 | } 630 | .from-column { 631 | background-image: url(http://static.daily.zhihu.com/img/News_Column_Entrance@2x.png) !important; 632 | background-size: 280px 30px; 633 | } 634 | .from-column:active { 635 | background-image: url(http://static.daily.zhihu.com/img/News_Column_Entrance_Highlight@2x.png) !important; 636 | } 637 | .night .from-column { 638 | color: #2b82ac; 639 | background-image: url(http://static.daily.zhihu.com/img/Dark_News_Column_Entrance@2x.png) !important; 640 | } 641 | .night .from-column:active { 642 | background-image: url(http://static.daily.zhihu.com/img/Dark_News_Column_Entrance_Highlight@2x.png) !important; 643 | } 644 | } 645 | .meta .meta-item { 646 | width: 39%; 647 | overflow: hidden; 648 | white-space: nowrap; 649 | text-overflow: ellipsis; 650 | display: inline-block; 651 | color: #929292; 652 | margin-right: 7px; 653 | } 654 | .headline .meta { 655 | white-space: nowrap; 656 | text-overflow: ellipsis; 657 | overflow: hidden; 658 | font-size: 11px; 659 | color: #b8b8b8; 660 | margin: 20px 0; 661 | padding: 0 20px; 662 | } 663 | .headline .meta a, 664 | .headline .meta a:hover { 665 | margin-top: 2px; 666 | float: right; 667 | font-size: 11px; 668 | color: #0066cf; 669 | text-decoration: none; 670 | } 671 | .answer h1, 672 | .answer h2, 673 | .answer h3, 674 | .answer h4, 675 | .answer h5 { 676 | font-size: 19px; 677 | } 678 | .origin-source, 679 | a.origin-source:link { 680 | display: block; 681 | margin: 25px 0; 682 | height: 50px; 683 | overflow: hidden; 684 | background: #f0f0f0; 685 | color: #888; 686 | position: relative; 687 | -webkit-touch-callout: none; 688 | } 689 | .origin-source .source-logo, 690 | a.origin-source:link .source-logo { 691 | float: left; 692 | width: 50px; 693 | height: 50px; 694 | margin-right: 10px; 695 | } 696 | .origin-source .text, 697 | a.origin-source:link .text { 698 | line-height: 50px; 699 | height: 50px; 700 | font-size: 13px; 701 | } 702 | .origin-source.with-link .text { 703 | color: #333; 704 | } 705 | .origin-source.with-link:after { 706 | display: block; 707 | position: absolute; 708 | border-color: transparent transparent transparent #f0f0f0; 709 | border-width: 7px; 710 | border-style: solid; 711 | height: 0; 712 | width: 0; 713 | top: 18px; 714 | right: 4px; 715 | line-height: 0; 716 | content: ""; 717 | } 718 | .origin-source.with-link:before { 719 | display: block; 720 | height: 0; 721 | width: 0; 722 | position: absolute; 723 | top: 18px; 724 | right: 3px; 725 | border-color: transparent transparent transparent #000; 726 | border-width: 7px; 727 | border-style: solid; 728 | line-height: 0; 729 | content: ""; 730 | } 731 | .origin-source-wrap { 732 | position: relative; 733 | background: #f0f0f0; 734 | } 735 | .origin-source-wrap .focus-link { 736 | position: absolute; 737 | right: 0; 738 | top: 0; 739 | width: 45px; 740 | color: #00a2ed; 741 | height: 50px; 742 | display: none; 743 | text-align: center; 744 | font-size: 12px; 745 | -webkit-touch-callout: none; 746 | } 747 | .origin-source-wrap .focus-link .btn-label { 748 | text-align: center; 749 | display: block; 750 | margin-top: 8px; 751 | border-left: solid 1px #ccc; 752 | height: 34px; 753 | line-height: 34px; 754 | } 755 | .origin-source-wrap.unfocused .focus-link { 756 | display: block; 757 | } 758 | .origin-source-wrap.unfocused .origin-source:before, 759 | .origin-source-wrap.unfocused .origin-source:after { 760 | display: none; 761 | } 762 | .night .origin-source-wrap { 763 | background: #292929; 764 | } 765 | .night .origin-source-wrap .focus-link { 766 | color: #116f9e; 767 | } 768 | .night .origin-source-wrap .btn-label { 769 | border-left: solid 1px #3f3f3f; 770 | } 771 | .night .origin-source, 772 | .night .origin-source.with-link { 773 | background: #292929; 774 | color: #666; 775 | } 776 | .night .origin-source .text, 777 | .night .origin-source.with-link .text { 778 | color: #666; 779 | } 780 | .night .origin-source.with-link:after { 781 | border-color: transparent transparent transparent #292929; 782 | } 783 | .night .origin-source.with-link:before { 784 | border-color: transparent transparent transparent #666; 785 | } 786 | --------------------------------------------------------------------------------