├── .gitignore
├── LICENSE
├── README.md
├── ZhihuDaily.iml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── me
│ │ └── chen_wei
│ │ └── zhihu
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── zhihu.css
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── me
│ │ │ └── chen_wei
│ │ │ └── zhihu
│ │ │ ├── Constants.java
│ │ │ ├── MyApplication.java
│ │ │ ├── cache
│ │ │ └── ACache.java
│ │ │ ├── event
│ │ │ ├── AllStoriedDownloadedEvent.java
│ │ │ ├── ContentsLoadedEvent.java
│ │ │ ├── LatestContentsLoadedEvent.java
│ │ │ ├── LoadContentEvent.java
│ │ │ ├── LoadFailureEvent.java
│ │ │ ├── NewsDownloadedEvent.java
│ │ │ ├── NewsLoadedEvent.java
│ │ │ └── TopStoriesLoadedEvent.java
│ │ │ ├── network
│ │ │ ├── api
│ │ │ │ └── ZhihuAPI.java
│ │ │ ├── model
│ │ │ │ ├── Contents.java
│ │ │ │ ├── Latest.java
│ │ │ │ └── News.java
│ │ │ └── processor
│ │ │ │ ├── ContentsProcessor.java
│ │ │ │ ├── IContentsProcessor.java
│ │ │ │ ├── INewsProcessor.java
│ │ │ │ ├── IOfflineDownloadProcessor.java
│ │ │ │ ├── NewsProcessor.java
│ │ │ │ └── OfflineDownloadProcessor.java
│ │ │ ├── presenter
│ │ │ ├── MainPresenter.java
│ │ │ └── StoryPresenter.java
│ │ │ ├── util
│ │ │ ├── DateUtil.java
│ │ │ └── NetworkUtil.java
│ │ │ └── views
│ │ │ ├── EndlessRecyclerViewScrollListener.java
│ │ │ ├── activities
│ │ │ ├── AboutMeActivity.java
│ │ │ ├── IMainActivity.java
│ │ │ ├── IStoryActivity.java
│ │ │ ├── MainActivity.java
│ │ │ └── StoryActivity.java
│ │ │ └── adapter
│ │ │ ├── StoryListAdapter.java
│ │ │ └── TopStoriesAdapter.java
│ ├── logo-web.png
│ └── res
│ │ ├── anim
│ │ └── hold.xml
│ │ ├── drawable-hdpi
│ │ ├── ic_action_download.png
│ │ ├── ic_launcher.png
│ │ └── ic_share.png
│ │ ├── drawable-mdpi
│ │ ├── ic_action_download.png
│ │ └── ic_share.png
│ │ ├── drawable-xhdpi
│ │ ├── ic_action_download.png
│ │ └── ic_share.png
│ │ ├── drawable-xxhdpi
│ │ ├── ic_action_download.png
│ │ └── ic_share.png
│ │ ├── drawable
│ │ ├── dot_bg_selector.xml
│ │ ├── point_bg_enable.xml
│ │ └── point_bg_normal.xml
│ │ ├── layout-night
│ │ └── item_story.xml
│ │ ├── layout
│ │ ├── activity_about_me.xml
│ │ ├── activity_main.xml
│ │ ├── activity_story.xml
│ │ ├── item_story.xml
│ │ └── item_top_story.xml
│ │ ├── menu
│ │ ├── menu_main.xml
│ │ └── menu_story_content.xml
│ │ ├── values-night-v21
│ │ └── styles.xml
│ │ ├── values-night
│ │ └── styles.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── me
│ └── chen_wei
│ └── zhihu
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── img
├── mine_1.jpg
├── mine_2.jpg
├── off_1.jpg
└── off_2.jpg
├── infiniteviewpager
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── zanlabs
│ │ └── widget
│ │ └── infiniteviewpager
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── zanlabs
│ │ └── widget
│ │ └── infiniteviewpager
│ │ ├── InfinitePagerAdapter.java
│ │ ├── InfiniteViewPager.java
│ │ ├── RecycleBin.java
│ │ ├── RecyclingPagerAdapter.java
│ │ └── indicator
│ │ ├── CirclePageIndicator.java
│ │ ├── LinePageIndicator.java
│ │ ├── PageIndicator.java
│ │ ├── TitlePageIndicator.java
│ │ └── UnderlinePageIndicator.java
│ └── res
│ ├── color
│ ├── vpi__dark_theme.xml
│ └── vpi__light_theme.xml
│ ├── drawable-hdpi
│ ├── vpi__tab_selected_focused_holo.9.png
│ ├── vpi__tab_selected_holo.9.png
│ ├── vpi__tab_selected_pressed_holo.9.png
│ ├── vpi__tab_unselected_focused_holo.9.png
│ ├── vpi__tab_unselected_holo.9.png
│ └── vpi__tab_unselected_pressed_holo.9.png
│ ├── drawable-mdpi
│ ├── vpi__tab_selected_focused_holo.9.png
│ ├── vpi__tab_selected_holo.9.png
│ ├── vpi__tab_selected_pressed_holo.9.png
│ ├── vpi__tab_unselected_focused_holo.9.png
│ ├── vpi__tab_unselected_holo.9.png
│ └── vpi__tab_unselected_pressed_holo.9.png
│ ├── drawable-xhdpi
│ ├── vpi__tab_selected_focused_holo.9.png
│ ├── vpi__tab_selected_holo.9.png
│ ├── vpi__tab_selected_pressed_holo.9.png
│ ├── vpi__tab_unselected_focused_holo.9.png
│ ├── vpi__tab_unselected_holo.9.png
│ └── vpi__tab_unselected_pressed_holo.9.png
│ ├── drawable
│ └── vpi__tab_indicator.xml
│ └── values
│ ├── strings.xml
│ ├── vpi__attrs.xml
│ ├── vpi__colors.xml
│ ├── vpi__defaults.xml
│ └── vpi__styles.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | lt application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/*.iml
33 | .gradle
34 | /local.properties
35 | /.idea/workspace.xml
36 | /.idea/libraries
37 | .DS_Store
38 | /build
39 | /captures
40 |
41 | .idea/
42 |
43 | *.iml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | pache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 知乎日报Android客户端(非官方版)
2 | 欢迎大家`Star`、`Fork`、`Issue`
3 |
4 | ## 声明
5 | ***项目仅供学习使用,请遵循知乎相关规定。如有侵权,告知后必定第一时间删除该项目。***
6 |
7 | ## 界面效果
8 | ***界面与官方风格相似度极高,遵循Material Design规范***
9 |
10 | | UI | 非官方 | 官方 |
11 | |----|-------|------|
12 | |主界面|||
13 | |文章界面|||
14 |
15 | ## 与官方知乎日报有哪些区别?
16 | * 没有烦人的推送
17 | * 权限极少
18 | * 不会常驻内存
19 | * 体积更小(2MB<5.3MB)
20 | * 给你最纯粹的阅读享受
21 | * ... ...
22 |
23 | ***好吧,我承认这些都是由于时间紧所有没来得及完成。。。***
24 |
25 |
26 | ## 学到了哪些?
27 | * 本项目参考了[Best practices in Android development](https://github.com/futurice/android-best-practices)给出的一些建议
28 | * Android `MVP` 模式的运用
29 | * 使用`Retrofit`获取RESTful API内容
30 | * `RecyclerView`的常规使用方法(下拉刷新,Endless Scrolling...)
31 | * `ViewPager`滚动效果
32 | * `WebView`加载HTML、CSS文件
33 | * `DayNight Theme`切换
34 | * ... ...
35 |
36 | **本项目适合对Android基础知识有一定了解但未做过项目的同学进行阅读**
37 |
38 |
39 | ## 后续将会增加的功能
40 | * `主题日报`
41 | * 日报内容根据日期进行分组
42 | * 监听网络状态
43 | * ... ...
44 |
45 | *注:由于最近学校事情较多,这些功能还不能及时完成,如有人有兴趣参与到项目中,欢迎PullRequest*
46 |
47 |
48 | ## 感谢
49 | * 感谢[Xiao Liang](https://github.com/izzyleung)分享的知乎日报API分析
50 | * 感谢[Square公司](http://square.github.io/)提供了众多非常优秀的开源库
51 |
52 | ## 关于作者
53 | * Website: [chen-wei.me](http://chen-wei.me)
54 | * Email: [hander_wei@163.com](hander_wei@163.com)
55 | * Github: [https://github.com/HanderWei](https://github.com/HanderWei)
56 | * 中国科学技术大学软件工程研究生在读
57 |
58 | ***现寻Android实习工作一份,如有相关工作机会,欢迎与我联系!***
59 |
60 |
61 | ## License
62 | The MIT License (MIT) Copyright (c) 2016 Chen Wei
63 |
64 | Permission is hereby granted, free of charge, to any person obtaining a copy of
65 | this software and associated documentation files (the "Software"), to deal in
66 | the Software without restriction, including without limitation the rights to
67 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
68 | of the Software, and to permit persons to whom the Software is furnished to do
69 | so, subject to the following conditions:
70 |
71 |
72 | The above copyright notice and this permission notice shall be included in all
73 | copies or substantial portions of the Software.
74 |
75 |
76 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
77 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
78 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
79 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
80 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
81 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
82 | SOFTWARE.
83 |
--------------------------------------------------------------------------------
/ZhihuDaily.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | def gitVersionCode() {
4 | def cmd = 'git rev-list HEAD --first-parent --count'
5 | cmd.execute().text.trim().toInteger()
6 | }
7 |
8 | def gitVersionTag() {
9 | def cmd = 'git describe --tags'
10 | def version = cmd.execute().text.trim()
11 |
12 | def pattern = "-(\\d+)-g"
13 | def matcher = version =~ pattern
14 |
15 | if (matcher) {
16 | version = version.substring(0, matcher.start()) + "." + matcher[0][1]
17 | } else {
18 | version = version + ".0"
19 | }
20 |
21 | return version
22 | }
23 |
24 | android {
25 | compileSdkVersion 23
26 | buildToolsVersion "23.0.1"
27 |
28 | defaultConfig {
29 | applicationId "me.chen_wei.zhihu"
30 | minSdkVersion 16
31 | targetSdkVersion 23
32 | versionCode 1
33 | versionName '1.0'
34 | }
35 | buildTypes {
36 | debug {
37 | // 为了不和 release 版本冲突
38 | applicationIdSuffix ".debug"
39 | }
40 | release {
41 | minifyEnabled false
42 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
43 | }
44 | applicationVariants.all { variant ->
45 | if (variant.buildType.name.equals('release')) {
46 | variant.mergedFlavor.versionCode = gitVersionCode()
47 | variant.mergedFlavor.versionName = gitVersionTag()
48 | }
49 | }
50 | }
51 | }
52 |
53 | dependencies {
54 | compile fileTree(dir: 'libs', include: ['*.jar'])
55 | testCompile 'junit:junit:4.12'
56 | compile 'com.android.support:appcompat-v7:23.3.0'
57 | compile 'com.android.support:design:23.3.0'
58 |
59 | //Network
60 | compile 'com.squareup.picasso:picasso:2.5.2'
61 | compile 'com.squareup.okhttp3:okhttp:3.0.1'
62 |
63 | //Butter Knife
64 | compile 'com.jakewharton:butterknife:7.0.1'
65 | //Event Bus
66 | compile 'de.greenrobot:eventbus:2.4.0'
67 | //Sectioned RecyclerView
68 | compile 'com.truizlop.sectionedrecyclerview:library:1.1.0'
69 | //RecyclerView
70 | compile 'com.android.support:recyclerview-v7:23.1.1'
71 | //CardView
72 | compile 'com.android.support:cardview-v7:23.1.1'
73 |
74 | //Retrofit
75 | compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
76 | compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
77 |
78 | compile 'com.bartoszlipinski.recyclerviewheader:library:1.2.1'
79 |
80 | debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1
81 | releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
82 | testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
83 |
84 | compile project(path: ':infiniteviewpager')
85 | }
86 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/Hander/Library/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/androidTest/java/me/chen_wei/zhihu/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu;
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 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
33 |
34 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HanderWei/ZhihuDaily/91aa686af04355915d52be968a870de5b6e37480/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/Constants.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu;
2 |
3 | /**
4 | * Created by Hander on 16/2/26.
5 | *
6 | * Email : hander_wei@163.com
7 | */
8 | public class Constants {
9 |
10 | public static final String API_URL = "http://news-at.zhihu.com/";
11 |
12 | public static final String STORY_URL = "http://daily.zhihu.com/story/";
13 |
14 | public static final String KEY_STORY_ID = "story_id";
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/MyApplication.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu;
2 |
3 | import android.app.Application;
4 | import android.support.v7.app.AppCompatDelegate;
5 |
6 | /**
7 | * Created by Hander on 16/2/28.
8 | *
9 | * Email : hander_wei@163.com
10 | */
11 | public class MyApplication extends Application {
12 |
13 | static{
14 | //设置DayNightTheme模式
15 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
16 | }
17 |
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 | // LeakCanary.install(this);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/AllStoriedDownloadedEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | /**
4 | * Created by Hander on 16/3/2.
5 | *
6 | * Email : hander_wei@163.com
7 | */
8 | public class AllStoriedDownloadedEvent {
9 |
10 | public AllStoriedDownloadedEvent() {
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/ContentsLoadedEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | import me.chen_wei.zhihu.network.model.Contents;
4 |
5 | /**
6 | * Created by Hander on 16/2/26.
7 | *
8 | * Email : hander_wei@163.com
9 | */
10 | public class ContentsLoadedEvent {
11 |
12 | public Contents contents;
13 |
14 | public ContentsLoadedEvent(Contents contents){
15 | this.contents = contents;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/LatestContentsLoadedEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | import me.chen_wei.zhihu.network.model.Contents;
4 |
5 | /**
6 | * Created by Hander on 16/3/2.
7 | *
8 | * Email : hander_wei@163.com
9 | */
10 | public class LatestContentsLoadedEvent {
11 |
12 | public Contents contents;
13 |
14 | public LatestContentsLoadedEvent(Contents contents) {
15 | this.contents = contents;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/LoadContentEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | /**
4 | * Created by Hander on 16/2/27.
5 | *
6 | * Email : hander_wei@163.com
7 | */
8 | public class LoadContentEvent {
9 |
10 | public int id;
11 |
12 | public LoadContentEvent(int id) {
13 | this.id = id;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/LoadFailureEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | /**
4 | * Created by Hander on 16/2/26.
5 | *
6 | * Email : hander_wei@163.com
7 | */
8 | public class LoadFailureEvent {
9 | public String msg;
10 |
11 | public LoadFailureEvent(String msg){
12 | this.msg = msg;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/NewsDownloadedEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | /**
4 | * Created by Hander on 16/3/2.
5 | *
6 | * Email : hander_wei@163.com
7 | */
8 | public class NewsDownloadedEvent {
9 |
10 | public NewsDownloadedEvent(){
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/NewsLoadedEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | import me.chen_wei.zhihu.network.model.News;
4 |
5 | /**
6 | * Created by Hander on 16/2/27.
7 | *
8 | * Email : hander_wei@163.com
9 | */
10 | public class NewsLoadedEvent {
11 |
12 | public News news;
13 |
14 | public NewsLoadedEvent(News news){
15 | this.news = news;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/event/TopStoriesLoadedEvent.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.event;
2 |
3 | import me.chen_wei.zhihu.network.model.Latest;
4 |
5 | /**
6 | * Created by Hander on 16/2/27.
7 | *
8 | * Email : hander_wei@163.com
9 | */
10 | public class TopStoriesLoadedEvent {
11 |
12 | public Latest latest;
13 |
14 | public TopStoriesLoadedEvent(Latest latest){
15 | this.latest = latest;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/network/api/ZhihuAPI.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.network.api;
2 |
3 | import me.chen_wei.zhihu.network.model.Contents;
4 | import me.chen_wei.zhihu.network.model.Latest;
5 | import me.chen_wei.zhihu.network.model.News;
6 | import retrofit2.Call;
7 | import retrofit2.http.GET;
8 | import retrofit2.http.Path;
9 |
10 | /**
11 | * Created by Hander on 16/2/26.
12 | *
13 | * Email : hander_wei@163.com
14 | */
15 | public interface ZhihuAPI {
16 |
17 | /*
18 | * 获取最新故事列表
19 | * 包含热门故事列表(5个)
20 | *
21 | * */
22 | @GET("api/4/news/latest")
23 | Call getLatestContent();
24 |
25 | //获取单条新闻内容
26 | @GET("api/4/news/{id}")
27 | Call getNews(@Path("id") int id);
28 |
29 | /*
30 | * 获取指定日期的故事列表
31 | *
32 | * Eg: date = 20160228
33 | * 则获取的是2016年2月27日的故事列表
34 | * */
35 | @GET("api/4/news/before/{date}")
36 | Call getContents(@Path("date") String date);
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/network/model/Contents.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.network.model;
2 |
3 | import java.io.Serializable;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by Hander on 16/2/27.
8 | *
9 | * Email : hander_wei@163.com
10 | */
11 | public class Contents implements Serializable{
12 |
13 |
14 | /**
15 | * date : 20160225
16 | * stories : [{"images":["http://pic3.zhimg.com/625c29a726de316807851e3af8bfd19a.jpg"],"type":0,"id":7915284,"ga_prefix":"022522","title":"深夜惊奇 · 赌球一时爽"},{"images":["http://pic4.zhimg.com/5a788559ae4069ef2a317154a4e875d7.jpg"],"type":0,"id":7920008,"ga_prefix":"022521","title":"这三部片的共同点是有很多液体,有热、有冷、有黏稠"},{"images":["http://pic2.zhimg.com/9a5c9bec1a56f41b5ad58e8c9c3e518d.jpg"],"type":0,"id":7875647,"ga_prefix":"022520","title":"这首叮当咣当的校铃,最早可能出自爱我中华的神剧"},{"images":["http://pic2.zhimg.com/291814ed80c1e407f8a0f5b67bd71339.jpg"],"type":0,"id":7902359,"ga_prefix":"022519","title":"为什么鼻子和嘴巴联通?这还要从鱼类说起\u2026\u2026"},{"images":["http://pic3.zhimg.com/191cde8bdbb36aa315d196546910ee66.jpg"],"type":0,"id":7918337,"ga_prefix":"022518","title":"男性在网络上的购买欲真的弱于女性吗?"},{"images":["http://pic3.zhimg.com/071b58fcf2d35444b749e40baf00c57a.jpg"],"type":0,"id":7888997,"ga_prefix":"022517","title":"我的专业是人类学,平时会去部落里当个女王啥的(误)"},{"images":["http://pic2.zhimg.com/aed859a292250f66690c80824e905315.jpg"],"type":0,"id":7919463,"ga_prefix":"022516","title":"「白米饭比可乐还容易让人血糖高」,还能吃米饭吗?"},{"images":["http://pic3.zhimg.com/aae31fef75f4d67e89c9a406d865199a.jpg"],"type":0,"id":7918982,"ga_prefix":"022515","title":"「我以为你都懂,可是你没有」"},{"images":["http://pic3.zhimg.com/eb526023315b1b74c3895fb6597ad10e.jpg"],"type":0,"id":7918297,"ga_prefix":"022514","title":"没错,一张「毛爷爷」一旦拆散,就花得更快了"},{"images":["http://pic1.zhimg.com/026fe4dd95ad206e1fefa594b9c1d9f8.jpg"],"type":0,"id":7918246,"ga_prefix":"022513","title":"萝卜只卖一角钱,居然因为太便宜被罚一万块"},{"images":["http://pic3.zhimg.com/cc60329095ece019f97ab68e6695396e.jpg"],"type":0,"id":7910806,"ga_prefix":"022512","title":"如何看待「日本专家炮轰中国新能源车政策」?"},{"images":["http://pic2.zhimg.com/05320e4ed8d74ba7fe2e9d544c63d121.jpg"],"type":0,"id":7918261,"ga_prefix":"022511","title":"为什么有的人喜欢责备别人并且得理不饶人?"},{"images":["http://pic2.zhimg.com/942e0d44ed0c5cf340ac7988d8b173b5.jpg"],"type":0,"id":7910403,"ga_prefix":"022510","title":"花钱支持歌手,这几个公司可能都会谢谢你"},{"images":["http://pic4.zhimg.com/d1bd811467ddff55f9640f04756d3173.jpg"],"type":0,"id":7884280,"ga_prefix":"022508","title":"都别争了,选爱我的还是我爱的有科学解释了"},{"images":["http://pic1.zhimg.com/dd912e5217e6c6f26ac93182a86893d8.jpg"],"type":0,"id":7915946,"ga_prefix":"022507","title":"《功夫熊猫 3》是中美合拍,负责人说中方主要做了这些"},{"images":["http://pic1.zhimg.com/1e87e49386f4c6161f8704e168b60948.jpg"],"type":0,"id":7917099,"ga_prefix":"022507","title":"外国人爱用的这些 app,中国人怎么也想不到"},{"images":["http://pic4.zhimg.com/997afbf82c668046d647fa5a78cf500f.jpg"],"type":0,"id":7914881,"ga_prefix":"022507","title":"这项规定一出台,苹果用户心里有点慌"},{"images":["http://pic1.zhimg.com/bb20b715b634f49322d3050c5e5b28cc.jpg"],"type":0,"id":7917162,"ga_prefix":"022507","title":"读读日报 24 小时热门:在战火纷飞的地道里走私 KFC"},{"images":["http://pic2.zhimg.com/722d059ececb8be03295a10d359f3e8d.jpg"],"type":0,"id":7907685,"ga_prefix":"022506","title":"瞎扯 · 多管闲事与不懂礼貌"}]
17 | */
18 |
19 | private String date;
20 | /**
21 | * images : ["http://pic3.zhimg.com/625c29a726de316807851e3af8bfd19a.jpg"]
22 | * type : 0
23 | * id : 7915284
24 | * ga_prefix : 022522
25 | * title : 深夜惊奇 · 赌球一时爽
26 | */
27 |
28 | private List stories;
29 |
30 | public void setDate(String date) {
31 | this.date = date;
32 | }
33 |
34 | public void setStories(List stories) {
35 | this.stories = stories;
36 | }
37 |
38 | public String getDate() {
39 | return date;
40 | }
41 |
42 | public List getStories() {
43 | return stories;
44 | }
45 |
46 | public static class StoriesEntity implements Serializable{
47 | private int type;
48 | private int id;
49 | private String ga_prefix;
50 | private String title;
51 | private List images;
52 |
53 | public void setType(int type) {
54 | this.type = type;
55 | }
56 |
57 | public void setId(int id) {
58 | this.id = id;
59 | }
60 |
61 | public void setGa_prefix(String ga_prefix) {
62 | this.ga_prefix = ga_prefix;
63 | }
64 |
65 | public void setTitle(String title) {
66 | this.title = title;
67 | }
68 |
69 | public void setImages(List images) {
70 | this.images = images;
71 | }
72 |
73 | public int getType() {
74 | return type;
75 | }
76 |
77 | public int getId() {
78 | return id;
79 | }
80 |
81 | public String getGa_prefix() {
82 | return ga_prefix;
83 | }
84 |
85 | public String getTitle() {
86 | return title;
87 | }
88 |
89 | public List getImages() {
90 | return images;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/network/model/Latest.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.network.model;
2 |
3 | import java.io.Serializable;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by Hander on 16/2/26.
8 | *
9 | * Email : hander_wei@163.com
10 | */
11 | public class Latest implements Serializable{
12 |
13 | /**
14 | * date : 20160226
15 | * stories : [{"images":["http://pic1.zhimg.com/f7c1de18d969350a86774230ab4a00b4.jpg"],"type":0,"id":7923064,"ga_prefix":"022619","title":"为什么近视的人不戴眼镜会听不清别人在说什么?"},{"images":["http://pic2.zhimg.com/1d5535f5becd198a26a5e4474c2f45cd.jpg"],"type":0,"id":7924329,"ga_prefix":"022618","title":"如果你身边有人想自杀,先看见并承认他的痛苦"},{"title":"穿着情趣制服招摇过市,这是我的第一次 Cosplay","ga_prefix":"022617","images":["http://pic3.zhimg.com/7701cb73c08c6bfe7eecb3c3b6752dde.jpg"],"multipic":true,"type":0,"id":7924059},{"images":["http://pic1.zhimg.com/6a7b6d2ce2543dece406c59bdb0ed0c0.jpg"],"type":0,"id":7919743,"ga_prefix":"022616","title":"比尔 · 盖茨夫妇写了封年度公开信,想要「改变世界的超能力」"},{"title":"简单来说,我的工作就是让你「看到」声音","ga_prefix":"022615","images":["http://pic3.zhimg.com/7db93f371bca7a18ad8c3c7ff53c130e.jpg"],"multipic":true,"type":0,"id":7923282},{"title":"为了好吃的涮羊肉,我一定要学会这些挑肉的方法","ga_prefix":"022614","images":["http://pic2.zhimg.com/e4419de35b1471985bf78745a28968b9.jpg"],"multipic":true,"type":0,"id":7912102},{"images":["http://pic3.zhimg.com/2799867f89aa16fe81ff8c1c840097fa.jpg"],"type":0,"id":7914085,"ga_prefix":"022613","title":"「错的不是我,是世界」,这里有了合理的解释"},{"images":["http://pic1.zhimg.com/b81e4222190f32a1cf0bde205be8d550.jpg"],"type":0,"id":7910087,"ga_prefix":"022612","title":"全世界最稀有的植物,我猜你们一定想听点新的"},{"images":["http://pic2.zhimg.com/0133a45257c460b2736dbe0fb8045c61.jpg"],"type":0,"id":7919331,"ga_prefix":"022611","title":"只要你用的是这五大银行,以后手机转账都免手续费"},{"images":["http://pic2.zhimg.com/668300e97c69ecc5b7c8f756505c762d.jpg"],"type":0,"id":7921049,"ga_prefix":"022610","title":"给艺人贴标签有时候是必要的,像王若琳这样的是异类"},{"images":["http://pic3.zhimg.com/b989d608c59d7156bc24d941fb8747ca.jpg"],"type":0,"id":7918511,"ga_prefix":"022609","title":"咦,为什么国外名校同学的成绩都那么好看?"},{"images":["http://pic3.zhimg.com/95dadb9e9fa901d9edc6d1e57211dab6.jpg"],"type":0,"id":7921365,"ga_prefix":"022608","title":"都上小学了还会尿床\u2026\u2026"},{"images":["http://pic2.zhimg.com/53b19dd1f7b6617f6b2a60a8fa088379.jpg"],"type":0,"id":7920604,"ga_prefix":"022607","title":"跟国外相比,北上广深成杭武南人口密度还不够大"},{"images":["http://pic1.zhimg.com/5e5521af241f9b77a1af501b70c8d314.jpg"],"type":0,"id":7920157,"ga_prefix":"022607","title":"从婴儿爽身粉致癌案谈起,为什么不推荐用爽身粉"},{"images":["http://pic4.zhimg.com/afce9eb8e6bd613c582ffd231425e8b3.jpg"],"type":0,"id":7919280,"ga_prefix":"022607","title":"留学之后你都形成了什么「坏习惯」?"},{"images":["http://pic3.zhimg.com/c04b5715b7a55d06b0ead498a6bef8d2.jpg"],"type":0,"id":7921768,"ga_prefix":"022607","title":"读读日报 24 小时热门:小李拿不到奥斯卡活该?求这烂梗快消失"},{"images":["http://pic3.zhimg.com/33459fa23c930a66f4dfe8de432a8106.jpg"],"type":0,"id":7900419,"ga_prefix":"022606","title":"瞎扯 · 如何正确地吐槽"}]
16 | * top_stories : [{"image":"http://pic1.zhimg.com/a18faea79ae2a851f2c6927e5d4b4f48.jpg","type":0,"id":7924059,"ga_prefix":"022617","title":"穿着情趣制服招摇过市,这是我的第一次 Cosplay"},{"image":"http://pic2.zhimg.com/06fdfa6a49051bf161f4703ed17657b5.jpg","type":0,"id":7919743,"ga_prefix":"022616","title":"比尔 · 盖茨夫妇写了封年度公开信,想要「改变世界的超能力」"},{"image":"http://pic3.zhimg.com/39c3b94db27347a3e3c72a3b5b375d4a.jpg","type":0,"id":7918511,"ga_prefix":"022609","title":"咦,为什么国外名校同学的成绩都那么好看?"},{"image":"http://pic2.zhimg.com/6d03c693d157cbf16966d8a929ad58d1.jpg","type":0,"id":7920157,"ga_prefix":"022607","title":"从婴儿爽身粉致癌案谈起,为什么不推荐用爽身粉"},{"image":"http://pic1.zhimg.com/82b3103761f9374ed0f1b2d6ffa0c134.jpg","type":0,"id":7920604,"ga_prefix":"022607","title":"跟国外相比,北上广深成杭武南人口密度还不够大"}]
17 | */
18 |
19 | private String date;
20 | /**
21 | * images : ["http://pic1.zhimg.com/f7c1de18d969350a86774230ab4a00b4.jpg"]
22 | * type : 0
23 | * id : 7923064
24 | * ga_prefix : 022619
25 | * title : 为什么近视的人不戴眼镜会听不清别人在说什么?
26 | */
27 |
28 | private List stories;
29 | /**
30 | * image : http://pic1.zhimg.com/a18faea79ae2a851f2c6927e5d4b4f48.jpg
31 | * type : 0
32 | * id : 7924059
33 | * ga_prefix : 022617
34 | * title : 穿着情趣制服招摇过市,这是我的第一次 Cosplay
35 | */
36 |
37 | private List top_stories;
38 |
39 | public void setDate(String date) {
40 | this.date = date;
41 | }
42 |
43 | public void setStories(List stories) {
44 | this.stories = stories;
45 | }
46 |
47 | public void setTop_stories(List top_stories) {
48 | this.top_stories = top_stories;
49 | }
50 |
51 | public String getDate() {
52 | return date;
53 | }
54 |
55 | public List getStories() {
56 | return stories;
57 | }
58 |
59 | public List getTop_stories() {
60 | return top_stories;
61 | }
62 |
63 | public static class StoriesEntity implements Serializable{
64 | private int type;
65 | private int id;
66 | private String ga_prefix;
67 | private String title;
68 | private List images;
69 |
70 | public void setType(int type) {
71 | this.type = type;
72 | }
73 |
74 | public void setId(int id) {
75 | this.id = id;
76 | }
77 |
78 | public void setGa_prefix(String ga_prefix) {
79 | this.ga_prefix = ga_prefix;
80 | }
81 |
82 | public void setTitle(String title) {
83 | this.title = title;
84 | }
85 |
86 | public void setImages(List images) {
87 | this.images = images;
88 | }
89 |
90 | public int getType() {
91 | return type;
92 | }
93 |
94 | public int getId() {
95 | return id;
96 | }
97 |
98 | public String getGa_prefix() {
99 | return ga_prefix;
100 | }
101 |
102 | public String getTitle() {
103 | return title;
104 | }
105 |
106 | public List getImages() {
107 | return images;
108 | }
109 | }
110 |
111 | public static class TopStoriesEntity implements Serializable{
112 | private String image;
113 | private int type;
114 | private int id;
115 | private String ga_prefix;
116 | private String title;
117 |
118 | public void setImage(String image) {
119 | this.image = image;
120 | }
121 |
122 | public void setType(int type) {
123 | this.type = type;
124 | }
125 |
126 | public void setId(int id) {
127 | this.id = id;
128 | }
129 |
130 | public void setGa_prefix(String ga_prefix) {
131 | this.ga_prefix = ga_prefix;
132 | }
133 |
134 | public void setTitle(String title) {
135 | this.title = title;
136 | }
137 |
138 | public String getImage() {
139 | return image;
140 | }
141 |
142 | public int getType() {
143 | return type;
144 | }
145 |
146 | public int getId() {
147 | return id;
148 | }
149 |
150 | public String getGa_prefix() {
151 | return ga_prefix;
152 | }
153 |
154 | public String getTitle() {
155 | return title;
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/app/src/main/java/me/chen_wei/zhihu/network/model/News.java:
--------------------------------------------------------------------------------
1 | package me.chen_wei.zhihu.network.model;
2 |
3 | import java.io.Serializable;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by Hander on 16/2/27.
8 | *
9 | * Email : hander_wei@163.com
10 | */
11 | public class News implements Serializable {
12 |
13 | /**
14 | * body :
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
ZARA、H&M、GAP 和优衣库各自的竞争优势是什么?
30 |
31 |
32 |
33 |
34 |
35 | 曹暘暘
36 |
37 |
38 |
39 |
我上个月刚刚在米兰参加了一个 ZARA 的买手和 Merchandising 的面试,终于看到了一个和自己专业相关的。本来已经很困了,看到这个问题立马来了精神。
然后是 GAP,我只想用一句话来形容它现在的处境:瘦死的骆驼比马大。作为曾经的时装界的销量霸主,GAP 居然在 2014 年被福布斯评为未来十年最后可能消失的十个时装品牌之一(你没有看错,还有最近在国内大火的美国的 AF,我那天放学回家室友就幸灾乐祸地跟我说的),GAP 今年刚刚找到我们专业帮助他们做一个项目,在意大利做品牌推广的,接了这个项目之后也查阅了很多关于 GAP 的资料。这里既然问到竞争优势,那么就不需要提商业模式了,倒也省了很多废话。
57 |
从设计来说,提到 GAP,往往都会想到那些简简单单的款式,牛仔裤,印着 LOGO 的上衣。但是今年 GAP 的广告充分显示了 GAP 以不变应万变的宗旨,GAP 的 Marketing Department Manager 来做 presentation 的时候说,GAP 是希望可以用简单的款式来塑造属于每个人的风格。但是如果看 GAP 的 facebook 和 instagram,你就会发现完全走的是小清新路线,其中很多设计还是比较符合 WGSN 上的潮流预测中的简约时尚。虽然最近几年 GAP 由于缺少固定风格难以满足不断求新求异的青少年消费群体,但是因为简约百搭的设计和优质的面料以及版型(尤其是裤子,我大爱),还是赢得了不少忠实消费者的青睐,再加上乘上了简约功能性为主导的潮流风格,使得设计方面算是勉强找到了一个灵魂归宿。在此奉上 GAP 的 Instagram 小清新截图一张。
58 |
另外,似乎知道了 Zara 和 H&M 一直被外界冠以环境污染的罪名,GAP 的联合创始人 Doris 和 Don Fisher 有意发起了“DO MORE THAN SELL CLOTHES”的倡议,似乎更要采用有机棉去促进农业可持续性生产,希望能够以品牌的社会使命唤起人们的品牌意识,重新树立品牌形象。
59 |
最后,采用那个 Manager 在做 presentation 时候的原话吧,GAP is confident not boastful; simple not boring; optimistic not delusional; courageous not radical; inclusive not lofty; youthful spirit not young; smart not smart-ass; current not trendy; classic not conservative; accessible not exclusive; liberating not revolutionary; human not heroic. 这几句话算是非常全面地体现了 GAP 的品牌定位和形象了。
60 |
-------------------------
61 |
H&M,另一个巨人。同样十分优秀的 fast fashion 品牌,在模式上面来说,更加倾向于兼顾出货时间和产品成本,因此速度不及 Zara,但是依靠着成本领先优势也在这一行业占有一席之地。时尚程度也不及 Zara,毕竟人家 Zara 也是抄大牌啊,而且抄到了精髓。H&M 是欧洲这边屌丝青年的首选,最近的设计偏街头,什么大印花,牛仔,大字母,大迷彩……产品缺点也很明显,质量不敢恭维,面料算是这四个品牌里面最差劲得了(洗过一次就知道了)。但是和 Zara 一样,都是把服装从耐用消费品变革为快速消费品的革新者,因此质量也就不那么重要了。
因此,可以说 H&M 和 Zara 是各自在不同的方面满足顾客需求,前者是在日常穿着和使用上,后者是在设计上。在运营上面,H&M 注重低成本,Zara 注重产品更新速度。
67 |
-------------------------
68 |
最后一个,优衣库。这是我在国内最喜欢的品牌,便宜,舒适,面料优质,款式简单容易搭配,后来增加了 UT 系列,我曾经一个夏天买了他们家 13 件 T 恤,就是为了不同的艺术家所创作的图案,有的特别喜欢的甚至都不舍得穿。在给 GAP 做 Competitor Analysis 的时候我第一个想到的就是优衣库。我曾经因为喜欢优衣库而买了两本柳井正的书以及他推荐的日本管理和经济学家大前研一的书,均获益匪浅。
Permission is hereby granted, free of charge, to any person obtaining a copy" +
47 | " of this software and associated documentation files (the \"Software\"), to deal" +
48 | " in the Software without restriction, including without limitation the rights" +
49 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell" +
50 | " copies of the Software, and to permit persons to whom the Software is" +
51 | " furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in" +
52 | " all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR" +
53 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY," +
54 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE" +
55 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER" +
56 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM," +
57 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN" +
58 | " THE SOFTWARE.
16 | * This class was taken from Android's implementation of {@link android.widget.AbsListView} which
17 | * is copyrighted 2006 The Android Open Source Project.
18 | */
19 | public class RecycleBin {
20 | /**
21 | * Views that were on screen at the start of layout. This array is populated at the start of
22 | * layout, and at the end of layout all view in activeViews are moved to scrapViews.
23 | * Views in activeViews represent a contiguous range of Views, with position of the first
24 | * view store in mFirstActivePosition.
25 | */
26 | private View[] activeViews = new View[0];
27 | private int[] activeViewTypes = new int[0];
28 |
29 | /** Unsorted views that can be used by the adapter as a convert view. */
30 | private SparseArray[] scrapViews;
31 |
32 | private int viewTypeCount;
33 |
34 | private SparseArray currentScrapViews;
35 |
36 | public void setViewTypeCount(int viewTypeCount) {
37 | if (viewTypeCount < 1) {
38 | throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
39 | }
40 | //noinspection unchecked
41 | SparseArray[] scrapViews = new SparseArray[viewTypeCount];
42 | for (int i = 0; i < viewTypeCount; i++) {
43 | scrapViews[i] = new SparseArray();
44 | }
45 | this.viewTypeCount = viewTypeCount;
46 | currentScrapViews = scrapViews[0];
47 | this.scrapViews = scrapViews;
48 | }
49 |
50 | protected boolean shouldRecycleViewType(int viewType) {
51 | return viewType >= 0;
52 | }
53 |
54 | /** @return A view from the ScrapViews collection. These are unordered. */
55 | View getScrapView(int position, int viewType) {
56 | if (viewTypeCount == 1) {
57 | return retrieveFromScrap(currentScrapViews, position);
58 | } else if (viewType >= 0 && viewType < scrapViews.length) {
59 | return retrieveFromScrap(scrapViews[viewType], position);
60 | }
61 | return null;
62 | }
63 |
64 | /**
65 | * Put a view into the ScrapViews list. These views are unordered.
66 | *
67 | * @param scrap The view to add
68 | */
69 | void addScrapView(View scrap, int position, int viewType) {
70 | if (viewTypeCount == 1) {
71 | currentScrapViews.put(position, scrap);
72 | } else {
73 | scrapViews[viewType].put(position, scrap);
74 | }
75 |
76 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
77 | scrap.setAccessibilityDelegate(null);
78 | }
79 | }
80 |
81 | /** Move all views remaining in activeViews to scrapViews. */
82 | void scrapActiveViews() {
83 | final View[] activeViews = this.activeViews;
84 | final int[] activeViewTypes = this.activeViewTypes;
85 | final boolean multipleScraps = viewTypeCount > 1;
86 |
87 | SparseArray scrapViews = currentScrapViews;
88 | final int count = activeViews.length;
89 | for (int i = count - 1; i >= 0; i--) {
90 | final View victim = activeViews[i];
91 | if (victim != null) {
92 | int whichScrap = activeViewTypes[i];
93 |
94 | activeViews[i] = null;
95 | activeViewTypes[i] = -1;
96 |
97 | if (!shouldRecycleViewType(whichScrap)) {
98 | continue;
99 | }
100 |
101 | if (multipleScraps) {
102 | scrapViews = this.scrapViews[whichScrap];
103 | }
104 | scrapViews.put(i, victim);
105 |
106 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
107 | victim.setAccessibilityDelegate(null);
108 | }
109 | }
110 | }
111 |
112 | pruneScrapViews();
113 | }
114 |
115 | /**
116 | * Makes sure that the size of scrapViews does not exceed the size of activeViews.
117 | * (This can happen if an adapter does not recycle its views).
118 | */
119 | private void pruneScrapViews() {
120 | final int maxViews = activeViews.length;
121 | final int viewTypeCount = this.viewTypeCount;
122 | final SparseArray[] scrapViews = this.scrapViews;
123 | for (int i = 0; i < viewTypeCount; ++i) {
124 | final SparseArray scrapPile = scrapViews[i];
125 | int size = scrapPile.size();
126 | final int extras = size - maxViews;
127 | size--;
128 | for (int j = 0; j < extras; j++) {
129 | scrapPile.remove(scrapPile.keyAt(size--));
130 | }
131 | }
132 | }
133 |
134 | static View retrieveFromScrap(SparseArray scrapViews, int position) {
135 | int size = scrapViews.size();
136 | if (size > 0) {
137 | // See if we still have a view for this position.
138 | for (int i = 0; i < size; i++) {
139 | int fromPosition = scrapViews.keyAt(i);
140 | View view = scrapViews.get(fromPosition);
141 | if (fromPosition == position) {
142 | scrapViews.remove(fromPosition);
143 | return view;
144 | }
145 | }
146 | int index = size - 1;
147 | View r = scrapViews.valueAt(index);
148 | scrapViews.remove(scrapViews.keyAt(index));
149 | return r;
150 | } else {
151 | return null;
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/infiniteviewpager/src/main/java/com/zanlabs/widget/infiniteviewpager/RecyclingPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zanlabs.widget.infiniteviewpager;
2 |
3 | import android.support.v4.view.PagerAdapter;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.AdapterView;
7 |
8 | /**
9 | * https://github.com/JakeWharton/salvage
10 | * A {@link android.support.v4.view.PagerAdapter} which behaves like an {@link android.widget.Adapter} with view types and
11 | * view recycling.
12 | */
13 | public abstract class RecyclingPagerAdapter extends PagerAdapter {
14 | static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
15 |
16 | private final RecycleBin recycleBin;
17 |
18 | public RecyclingPagerAdapter() {
19 | this(new RecycleBin());
20 | }
21 |
22 | RecyclingPagerAdapter(RecycleBin recycleBin) {
23 | this.recycleBin = recycleBin;
24 | recycleBin.setViewTypeCount(getViewTypeCount());
25 | }
26 |
27 | @Override
28 | public void notifyDataSetChanged() {
29 | recycleBin.scrapActiveViews();
30 | super.notifyDataSetChanged();
31 | }
32 |
33 | @Override
34 | public final Object instantiateItem(ViewGroup container, int position) {
35 | int viewType = getItemViewType(position);
36 | View view = null;
37 | if (viewType != IGNORE_ITEM_VIEW_TYPE) {
38 | view = recycleBin.getScrapView(position, viewType);
39 | }
40 | view = getViewInternal(position, view, container);
41 | container.addView(view);
42 | return view;
43 | }
44 |
45 | @Override
46 | public final void destroyItem(ViewGroup container, int position, Object object) {
47 | View view = (View) object;
48 | container.removeView(view);
49 | int viewType = getItemViewType(position);
50 | if (viewType != IGNORE_ITEM_VIEW_TYPE) {
51 | recycleBin.addScrapView(view, position, viewType);
52 | }
53 | }
54 |
55 | protected View getViewInternal(int position, View convertView, ViewGroup container){
56 | return getView(position,convertView,container);
57 | }
58 |
59 | @Override
60 | public final boolean isViewFromObject(View view, Object object) {
61 | return view == object;
62 | }
63 |
64 | /**
65 | *
66 | * Returns the number of types of Views that will be created by
67 | * {@link #getView}. Each type represents a set of views that can be
68 | * converted in {@link #getView}. If the adapter always returns the same
69 | * type of View for all items, this method should return 1.
70 | *
71 | *
72 | * This method will only be called when when the adapter is set on the
73 | * the {@link android.widget.AdapterView}.
74 | *
75 | *
76 | * @return The number of types of Views that will be created by this adapter
77 | */
78 | public int getViewTypeCount() {
79 | return 1;
80 | }
81 |
82 | /**
83 | * Get the type of View that will be created by {@link #getView} for the specified item.
84 | *
85 | * @param position The position of the item within the adapter's data set whose view type we
86 | * want.
87 | * @return An integer representing the type of View. Two views should share the same type if one
88 | * can be converted to the other in {@link #getView}. Note: Integers must be in the
89 | * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
90 | * also be returned.
91 | * @see #IGNORE_ITEM_VIEW_TYPE
92 | */
93 | @SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses.
94 | public int getItemViewType(int position) {
95 | return 0;
96 | }
97 |
98 | /**
99 | * Get a View that displays the data at the specified position in the data set. You can either
100 | * create a View manually or inflate it from an XML layout file. When the View is inflated, the
101 | * parent View (GridView, ListView...) will apply default layout parameters unless you use
102 | * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
103 | * to specify a root view and to prevent attachment to the root.
104 | *
105 | * @param position The position of the item within the adapter's data set of the item whose view
106 | * we want.
107 | * @param convertView The old view to reuse, if possible. Note: You should check that this view
108 | * is non-null and of an appropriate type before using. If it is not possible to convert
109 | * this view to display the correct data, this method can create a new view.
110 | * Heterogeneous lists can specify their number of view types, so that this View is
111 | * always of the right type (see {@link #getViewTypeCount()} and
112 | * {@link #getItemViewType(int)}).
113 | * @param container The parent that this view will eventually be attached to
114 | * @return A View corresponding to the data at the specified position.
115 | */
116 | public abstract View getView(int position, View convertView, ViewGroup container);
117 | }
118 |
--------------------------------------------------------------------------------
/infiniteviewpager/src/main/java/com/zanlabs/widget/infiniteviewpager/indicator/PageIndicator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 Patrik Akerfeldt
3 | * Copyright (C) 2011 Jake Wharton
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.zanlabs.widget.infiniteviewpager.indicator;
19 |
20 | import android.support.v4.view.ViewPager;
21 |
22 | /**
23 | * A PageIndicator is responsible to show an visual indicator on the total views
24 | * number and the current visible view.
25 | */
26 | public interface PageIndicator extends ViewPager.OnPageChangeListener {
27 | /**
28 | * Bind the indicator to a ViewPager.
29 | *
30 | * @param view
31 | */
32 | void setViewPager(ViewPager view);
33 |
34 | /**
35 | * Bind the indicator to a ViewPager.
36 | *
37 | * @param view
38 | * @param initialPosition
39 | */
40 | void setViewPager(ViewPager view, int initialPosition);
41 |
42 | /**
43 | *
Set the current page of both the ViewPager and indicator.
44 | *
45 | *
This must be used if you need to set the page before
46 | * the views are drawn on screen (e.g., default start page).