├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── Gank.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jin │ │ └── gank │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── jin │ │ └── gank │ │ ├── BaseActivity.java │ │ ├── GankActivity.java │ │ ├── MainActivity.java │ │ ├── PictureActivity.java │ │ ├── WebActivity.java │ │ ├── adapter │ │ ├── AnimRecyclerViewAdapter.java │ │ └── GankListAdapter.java │ │ ├── application │ │ └── MyApplication.java │ │ ├── data │ │ ├── Constant.java │ │ ├── DataBinder.java │ │ ├── GankCategory.java │ │ ├── GankDay.java │ │ └── GankRandom.java │ │ ├── decoration │ │ ├── DividerGridItemDecoration.java │ │ └── DividerLinearItemDecoration.java │ │ ├── fragment │ │ ├── GankFragment.java │ │ ├── MyFragment.java │ │ └── MyImgFragment.java │ │ ├── network │ │ ├── RESTApi.java │ │ └── RetrofitHelp.java │ │ ├── util │ │ ├── DateUtils.java │ │ ├── ImageUtils.java │ │ ├── LoveStringUtils.java │ │ ├── Once.java │ │ ├── ShareUtils.java │ │ ├── StringStyleUtils.java │ │ ├── TaskUtils.java │ │ └── ToastUtils.java │ │ └── view │ │ ├── GoodAppBarLayout.java │ │ ├── LoveVideoView.java │ │ ├── PLAImageView.java │ │ ├── RatioImageView.java │ │ └── VideoImageView.java │ └── res │ ├── anim │ └── slide_in_right.xml │ ├── drawable │ ├── gride_divider.xml │ └── line_divider.xml │ ├── layout │ ├── activity_gank.xml │ ├── activity_main.xml │ ├── activity_picture.xml │ ├── activity_web.xml │ ├── common_main_toolbar.xml │ ├── common_toolbar.xml │ ├── common_toolbar_with_indicator.xml │ ├── common_toolbar_with_title_view.xml │ ├── fragment_category.xml │ ├── fragment_gank.xml │ ├── fragment_img.xml │ ├── item_category.xml │ ├── item_gank.xml │ ├── item_img.xml │ ├── view_empty.xml │ ├── view_more_progress.xml │ └── view_video.xml │ ├── menu │ ├── menu_gank.xml │ ├── menu_main.xml │ └── menu_picture.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ ├── dropdown.png │ ├── ic_launcher.png │ ├── ic_play.png │ └── ic_share_white_24dp.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── android_material_design_colours.xml │ ├── arrays.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imgs ├── big.png ├── category.png └── girl.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /local.properties 3 | /.idea 4 | /gradle 5 | /build 6 | /Gank.iml -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 1.8 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /Gank.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gank 2 | 该项目是工作之余做得一个小项目 其中接口采用了干活集中营api开发 详情:http://gank.io/api 其中有些功能参考了https://github.com/drakeet/Meizhi项目 3 | 4 | 项目功能:提供各类开发所需的资料 包括开源项目的github地址 5 | 6 | 整体框架采用 lambda+retrofit+okhttp+rxandroid+Glide+DataBinding 并编写网络请求api 基本上符合material design设计 7 | 8 | 2015-08-25 新增ToolBar 和 TabLayout选项卡
9 | 10 | 2015-08-26 新增SuperRecyclerView瀑布流图片加载
11 | 12 | 2015-08-27 首页福利妹子图片加载模块功能完成 点击展示大图
13 | 14 | 2015-08-28 完成分类模块数据展示
15 | 16 | 2015-08-31 新增点击妹子item进入每日详情页面
17 | 18 | 2015-09-09 将Retrofit切换到2.0版本
19 | 20 | 2015-10-29 新增Android DataBinding 21 | 22 | 现在只完成了1.0版本 接下来有空还会添加新功能 如果你们有好的想法可以直接fork直接更新 23 | 24 | 下面直接上图 25 | 26 | ![image](https://github.com/jtsky/gank/blob/master/imgs/girl.png) 27 | ![image](https://github.com/jtsky/gank/blob/master/imgs/category.png) 28 | ![image](https://github.com/jtsky/gank/blob/master/imgs/big.png) 29 | 30 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /app.iml -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 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 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda'//lambda表达式插件 3 | //apply plugin: 'jsonschema2pojo' 4 | //apply plugin: 'com.android.databinding' 5 | android { 6 | compileSdkVersion 23 7 | buildToolsVersion '23.0.3' 8 | 9 | defaultConfig { 10 | applicationId "com.jin.gank" 11 | minSdkVersion 14 12 | targetSdkVersion 23 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | 17 | dataBinding { 18 | enabled = true 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | 33 | packagingOptions { 34 | exclude 'META-INF/DEPENDENCIES.txt' 35 | exclude 'META-INF/LICENSE.txt' 36 | exclude 'META-INF/NOTICE.txt' 37 | exclude 'META-INF/NOTICE' 38 | exclude 'META-INF/LICENSE' 39 | exclude 'META-INF/DEPENDENCIES' 40 | exclude 'META-INF/notice.txt' 41 | exclude 'META-INF/license.txt' 42 | exclude 'META-INF/dependencies.txt' 43 | exclude 'META-INF/LGPL2.1' 44 | } 45 | } 46 | 47 | dependencies { 48 | compile fileTree(include: ['*.jar'], dir: 'libs') 49 | compile 'com.android.support:design:23.2.1' 50 | compile 'com.android.support:cardview-v7:23.2.1' 51 | //网络访问框架 52 | compile 'com.squareup.retrofit2:retrofit:2.0.0' 53 | compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0' 54 | compile 'com.squareup.retrofit2:converter-gson:2.0.0' 55 | //网络请求框架 56 | compile 'com.squareup.okhttp3:okhttp:3.2.0' 57 | compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0' 58 | //响应式编程 59 | compile 'io.reactivex:rxandroid:1.0.1' 60 | //注解框架 61 | compile 'com.jakewharton:butterknife:7.0.1' 62 | //图片加载框架 63 | compile 'com.github.bumptech.glide:glide:3.6.1' 64 | //compile 'com.squareup.picasso:picasso:2.5.2' 65 | //一个高效的TabLayout库 66 | compile 'com.ogaclejapan.smarttablayout:library:1.3.0' 67 | //TabLayout帮助库 68 | compile 'com.ogaclejapan.smarttablayout:utils-v4:1.3.0@aar' 69 | //activity帮助类库 70 | compile 'com.vlonjatg.android:progress-activity:1.1.1' 71 | compile 'com.malinskiy:superrecyclerview:1.1.1' 72 | compile 'jp.wasabeef:recyclerview-animators:2.0.1' 73 | compile 'com.commit451:PhotoView:1.2.4' 74 | //进度条 75 | compile 'com.daimajia.numberprogressbar:library:1.2@aar' 76 | 77 | } 78 | -------------------------------------------------------------------------------- /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:\Program Files (x86)\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/com/jin/gank/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.AppBarLayout; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | import android.view.animation.DecelerateInterpolator; 8 | 9 | import butterknife.Bind; 10 | import butterknife.ButterKnife; 11 | 12 | /** 13 | * Created by Administrator on 2015/8/27. 14 | */ 15 | public abstract class BaseActivity extends AppCompatActivity { 16 | public String[] getmCategoryArray() { 17 | return mCategoryArray; 18 | } 19 | 20 | private String[] mCategoryArray; 21 | @Bind(R.id.AppBarLayout) 22 | AppBarLayout mAppBarLayout; 23 | @Bind(R.id.common_toolbar) 24 | Toolbar mToolbar; 25 | 26 | protected boolean mIsHiddenToolBar = false; 27 | 28 | abstract protected int provideContentViewId(); 29 | 30 | @Override 31 | public void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(provideContentViewId()); 34 | mCategoryArray = getResources().getStringArray(R.array.category_list); 35 | ButterKnife.bind(this); 36 | setToolBar(); 37 | } 38 | 39 | private void setToolBar() { 40 | if (null != mToolbar) { 41 | setSupportActionBar(mToolbar); 42 | getSupportActionBar().setHomeButtonEnabled(false);//决定左上角的图标是否可以点击 43 | getSupportActionBar().setDisplayHomeAsUpEnabled(false);//给左上角图标的左边加上一个返回的图标 44 | mToolbar.setTitle(R.string.app_name); 45 | } 46 | } 47 | 48 | 49 | protected void setAppBar(float alpha) { 50 | getSupportActionBar().setHomeButtonEnabled(true);//决定左上角的图标是否可以点击 51 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);//给左上角图标的左边加上一个返回的图标 52 | mAppBarLayout.setAlpha(alpha); 53 | mToolbar.setNavigationOnClickListener(v -> finish()); 54 | } 55 | 56 | protected void hideOrShowToolbar() { 57 | mAppBarLayout.animate() 58 | .translationY(mIsHiddenToolBar ? 0 : -mAppBarLayout.getHeight()) 59 | .setInterpolator(new DecelerateInterpolator(2)) 60 | .start(); 61 | 62 | mIsHiddenToolBar = !mIsHiddenToolBar; 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/GankActivity.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.Bundle; 5 | import android.support.design.widget.TabLayout; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v4.app.FragmentPagerAdapter; 9 | import android.support.v4.view.ViewPager; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.view.animation.DecelerateInterpolator; 14 | 15 | import com.jin.gank.fragment.GankFragment; 16 | 17 | import java.text.DateFormat; 18 | import java.text.SimpleDateFormat; 19 | import java.util.List; 20 | 21 | import butterknife.Bind; 22 | import butterknife.ButterKnife; 23 | 24 | 25 | public class GankActivity extends BaseActivity implements ViewPager.OnPageChangeListener { 26 | private static final String TAG = "GankActivity"; 27 | public static final String EXTRA_GANK_DATA = "gank_data"; 28 | 29 | @Bind(R.id.pager) 30 | ViewPager mViewPager; 31 | @Bind(R.id.tabLayout) 32 | TabLayout mTabLayout; 33 | GankPagerAdapter mPagerAdapter; 34 | List mDates; 35 | 36 | 37 | @Override 38 | protected int provideContentViewId() { 39 | return R.layout.activity_gank; 40 | } 41 | 42 | 43 | @Override 44 | public void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | ButterKnife.bind(this); 47 | mDates = getIntent().getStringArrayListExtra(EXTRA_GANK_DATA); 48 | 49 | setTitle(mDates.get(0)); 50 | setAppBar(1.0f); 51 | initViewPager(); 52 | initTabLayout(); 53 | } 54 | 55 | private void initViewPager() { 56 | mPagerAdapter = new GankPagerAdapter(getSupportFragmentManager(), mDates); 57 | mViewPager.setAdapter(mPagerAdapter); 58 | mViewPager.setOffscreenPageLimit(5);//缓存ViewPager页面数 59 | mViewPager.addOnPageChangeListener(this); 60 | } 61 | 62 | private void initTabLayout() { 63 | for (int i = 0; i < mPagerAdapter.getCount(); i++) { 64 | mTabLayout.addTab(mTabLayout.newTab()); 65 | } 66 | mTabLayout.setupWithViewPager(mViewPager); 67 | } 68 | 69 | @Override 70 | public void onConfigurationChanged(Configuration newConfig) { 71 | super.onConfigurationChanged(newConfig); 72 | if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { 73 | hideOrShowToolbar(); 74 | } else { 75 | hideOrShowToolbar(); 76 | } 77 | } 78 | 79 | @Override 80 | protected void hideOrShowToolbar() { 81 | View toolbar = findViewById(R.id.toolbar_with_indicator); 82 | toolbar.animate() 83 | .translationY(mIsHiddenToolBar ? 0 : -mToolbar.getHeight()) 84 | .setInterpolator(new DecelerateInterpolator(2)) 85 | .start(); 86 | mIsHiddenToolBar = !mIsHiddenToolBar; 87 | if (mIsHiddenToolBar) { 88 | mViewPager.setTag(mViewPager.getPaddingTop()); 89 | mViewPager.setPadding(0, 0, 0, 0); 90 | } else { 91 | mViewPager.setPadding(0, (int) mViewPager.getTag(), 0, 0); 92 | mViewPager.setTag(null); 93 | } 94 | } 95 | 96 | 97 | @Override 98 | public boolean onCreateOptionsMenu(Menu menu) { 99 | getMenuInflater().inflate(R.menu.menu_main, menu); 100 | return true; 101 | } 102 | 103 | @Override 104 | public boolean onOptionsItemSelected(MenuItem item) { 105 | int id = item.getItemId(); 106 | switch (id) { 107 | case R.id.action_about: 108 | // startActivity(new Intent(this, AboutActivity.class)); 109 | return true; 110 | 111 | } 112 | return super.onOptionsItemSelected(item); 113 | } 114 | 115 | 116 | @Override 117 | protected void onDestroy() { 118 | mViewPager.removeOnPageChangeListener(this); 119 | super.onDestroy(); 120 | ButterKnife.unbind(this); 121 | } 122 | 123 | @Override 124 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 125 | 126 | } 127 | 128 | @Override 129 | public void onPageSelected(int position) { 130 | setTitle(mDates.get(position)); 131 | } 132 | 133 | @Override 134 | public void onPageScrollStateChanged(int state) { 135 | 136 | } 137 | 138 | public class GankPagerAdapter extends FragmentPagerAdapter { 139 | List mDates; 140 | DateFormat dfm; 141 | 142 | public GankPagerAdapter(FragmentManager fm, List dates) { 143 | super(fm); 144 | mDates = dates; 145 | dfm = new SimpleDateFormat("yyyy-MM-dd"); 146 | } 147 | 148 | @Override 149 | public Fragment getItem(int position) { 150 | String[] date = mDates.get(position).split("-"); 151 | return GankFragment.newInstance(Integer.parseInt(date[0]), Integer.parseInt(date[1]), Integer.parseInt(date[2])); 152 | } 153 | 154 | @Override 155 | public int getCount() { 156 | return mDates.size(); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.view.ViewPager; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | import com.jin.gank.fragment.MyFragment; 8 | import com.jin.gank.fragment.MyImgFragment; 9 | import com.ogaclejapan.smarttablayout.SmartTabLayout; 10 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter; 11 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems; 12 | 13 | import butterknife.Bind; 14 | import butterknife.ButterKnife; 15 | 16 | public class MainActivity extends BaseActivity { 17 | private static final String TAG = "MainActivity"; 18 | @Bind(R.id.smartTabLayout) 19 | SmartTabLayout mSmartTabLayout; 20 | @Bind(R.id.viewpager) 21 | ViewPager mViewpager; 22 | 23 | 24 | @Override 25 | protected int provideContentViewId() { 26 | return R.layout.activity_main; 27 | } 28 | 29 | @Override 30 | public void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | ButterKnife.bind(this); 33 | FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter( 34 | getSupportFragmentManager(), FragmentPagerItems.with(this) 35 | .add(getmCategoryArray()[0], MyImgFragment.class) 36 | .add(getmCategoryArray()[1], MyFragment.class) 37 | .add(getmCategoryArray()[2], MyFragment.class) 38 | .add(getmCategoryArray()[3], MyFragment.class) 39 | .add(getmCategoryArray()[4], MyFragment.class) 40 | .add(getmCategoryArray()[5], MyFragment.class) 41 | .add(getmCategoryArray()[6], MyFragment.class) 42 | .create()); 43 | mViewpager.setAdapter(adapter); 44 | mViewpager.setOffscreenPageLimit(adapter.getCount());//设置ViewPager缓存个数 45 | mSmartTabLayout.setViewPager(mViewpager); 46 | } 47 | 48 | 49 | @Override 50 | public boolean onCreateOptionsMenu(Menu menu) { 51 | getMenuInflater().inflate(R.menu.menu_main, menu); 52 | return true; 53 | } 54 | 55 | @Override 56 | public boolean onOptionsItemSelected(MenuItem item) { 57 | if (item.getItemId() == R.id.action_about) { 58 | return true; 59 | } else { 60 | return super.onOptionsItemSelected(item); 61 | } 62 | } 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/PictureActivity.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.view.ViewCompat; 5 | import android.support.v7.app.AlertDialog; 6 | import android.view.Menu; 7 | import android.view.MenuItem; 8 | import android.widget.ImageView; 9 | 10 | import com.bumptech.glide.Glide; 11 | import com.jin.gank.util.ImageUtils; 12 | import com.jin.gank.util.ShareUtils; 13 | 14 | import butterknife.Bind; 15 | import butterknife.ButterKnife; 16 | import uk.co.senab.photoview.PhotoViewAttacher; 17 | 18 | public class PictureActivity extends BaseActivity { 19 | 20 | public static final String EXTRA_IMAGE_URL = "image_url"; 21 | public static final String EXTRA_IMAGE_TITLE = "image_title"; 22 | public static final String TRANSIT_PIC = "picture"; 23 | 24 | @Bind(R.id.picture) ImageView mImageView; 25 | 26 | PhotoViewAttacher mPhotoViewAttacher; 27 | String mImageUrl, mImageTitle; 28 | 29 | private void parseIntent() { 30 | mImageUrl = getIntent().getStringExtra(EXTRA_IMAGE_URL); 31 | mImageTitle = getIntent().getStringExtra(EXTRA_IMAGE_TITLE); 32 | } 33 | 34 | 35 | @Override 36 | protected int provideContentViewId() { 37 | return R.layout.activity_picture; 38 | } 39 | 40 | @Override public void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | ButterKnife.bind(this); 43 | parseIntent(); 44 | 45 | // init image view 46 | ViewCompat.setTransitionName(mImageView, TRANSIT_PIC); 47 | Glide.with(this).load(mImageUrl).into(mImageView); 48 | 49 | setAppBar(0.7f); 50 | setTitle(mImageTitle); 51 | 52 | setUpPhotoAttacher(); 53 | } 54 | 55 | private void setUpPhotoAttacher() { 56 | mPhotoViewAttacher = new PhotoViewAttacher(mImageView); 57 | mPhotoViewAttacher.setOnViewTapListener((view, v, v1) -> hideOrShowToolbar()); 58 | mPhotoViewAttacher.setOnLongClickListener(v -> { 59 | new AlertDialog.Builder(PictureActivity.this).setMessage( 60 | getString(R.string.saving_picture)) 61 | .setNegativeButton(android.R.string.cancel, (dialog, which) -> { 62 | dialog.dismiss(); 63 | }) 64 | .setPositiveButton(android.R.string.ok, (dialog, which) -> { 65 | saveImageToGallery(); 66 | dialog.dismiss(); 67 | }) 68 | .show(); 69 | return true; 70 | }); 71 | } 72 | 73 | private void saveImageToGallery() { 74 | ImageUtils.saveImageToSdCard(this, mImageUrl, mImageTitle); 75 | } 76 | 77 | @Override public boolean onCreateOptionsMenu(Menu menu) { 78 | getMenuInflater().inflate(R.menu.menu_picture, menu); 79 | return true; 80 | } 81 | 82 | @Override public boolean onOptionsItemSelected(MenuItem item) { 83 | int id = item.getItemId(); 84 | switch (id) { 85 | case R.id.action_share: 86 | ShareUtils.share(this); 87 | return true; 88 | } 89 | 90 | return super.onOptionsItemSelected(item); 91 | } 92 | 93 | @Override protected void onDestroy() { 94 | super.onDestroy(); 95 | ButterKnife.unbind(this); 96 | } 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/WebActivity.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.text.TextUtils; 6 | import android.view.KeyEvent; 7 | import android.webkit.WebChromeClient; 8 | import android.webkit.WebSettings; 9 | import android.webkit.WebView; 10 | import android.webkit.WebViewClient; 11 | import android.widget.TextSwitcher; 12 | import android.widget.TextView; 13 | 14 | import com.daimajia.numberprogressbar.NumberProgressBar; 15 | 16 | import butterknife.Bind; 17 | import butterknife.ButterKnife; 18 | 19 | public class WebActivity extends BaseActivity { 20 | 21 | public static final String EXTRA_URL = "extra_url"; 22 | public static final String EXTRA_TITLE = "extra_title"; 23 | 24 | @Bind(R.id.progressbar) NumberProgressBar mProgressbar; 25 | @Bind(R.id.webView) WebView mWebView; 26 | @Bind(R.id.tv_title) TextSwitcher mTextSwitcher; 27 | 28 | Context mContext; 29 | String mUrl, mTitle; 30 | 31 | @Override protected int provideContentViewId() { 32 | return R.layout.activity_web; 33 | } 34 | 35 | 36 | 37 | @Override public void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | ButterKnife.bind(this); 40 | mContext = this; 41 | mUrl = getIntent().getStringExtra(EXTRA_URL); 42 | mTitle = getIntent().getStringExtra(EXTRA_TITLE); 43 | setAppBar(1.0f); 44 | WebSettings settings = mWebView.getSettings(); 45 | settings.setJavaScriptEnabled(true); 46 | settings.setLoadWithOverviewMode(true); 47 | settings.setAppCacheEnabled(true); 48 | settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); 49 | settings.setSupportZoom(true); 50 | mWebView.setWebChromeClient(new ChromeClient()); 51 | mWebView.setWebViewClient(new LoveClient()); 52 | 53 | mWebView.loadUrl(mUrl); 54 | 55 | mTextSwitcher.setFactory(() -> { 56 | TextView textView = new TextView(this); 57 | textView.setTextAppearance(this, R.style.WebTitle); 58 | textView.setSingleLine(true); 59 | textView.setEllipsize(TextUtils.TruncateAt.MARQUEE); 60 | textView.postDelayed(() -> textView.setSelected(true), 1738); 61 | return textView; 62 | }); 63 | mTextSwitcher.setInAnimation(this, android.R.anim.fade_in); 64 | mTextSwitcher.setOutAnimation(this, android.R.anim.fade_out); 65 | if (mTitle != null) setTitle(mTitle); 66 | } 67 | 68 | @Override public void setTitle(CharSequence title) { 69 | super.setTitle(title); 70 | mTextSwitcher.setText(title); 71 | } 72 | 73 | @Override public boolean onKeyDown(int keyCode, KeyEvent event) { 74 | if (event.getAction() == KeyEvent.ACTION_DOWN) { 75 | switch (keyCode) { 76 | case KeyEvent.KEYCODE_BACK: 77 | if (mWebView.canGoBack()) { 78 | mWebView.goBack(); 79 | } 80 | else { 81 | finish(); 82 | } 83 | return true; 84 | } 85 | } 86 | return super.onKeyDown(keyCode, event); 87 | } 88 | 89 | @Override protected void onDestroy() { 90 | super.onDestroy(); 91 | if (mWebView != null) mWebView.destroy(); 92 | ButterKnife.unbind(this); 93 | } 94 | 95 | @Override protected void onPause() { 96 | if (mWebView != null) mWebView.onPause(); 97 | super.onPause(); 98 | } 99 | 100 | @Override protected void onResume() { 101 | super.onResume(); 102 | if (mWebView != null) mWebView.onResume(); 103 | } 104 | 105 | private class ChromeClient extends WebChromeClient { 106 | 107 | @Override public void onProgressChanged(WebView view, int newProgress) { 108 | super.onProgressChanged(view, newProgress); 109 | mProgressbar.setProgress(newProgress); 110 | } 111 | 112 | @Override public void onReceivedTitle(WebView view, String title) { 113 | super.onReceivedTitle(view, title); 114 | setTitle(title); 115 | } 116 | } 117 | 118 | private class LoveClient extends WebViewClient { 119 | 120 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 121 | if (url != null) view.loadUrl(url); 122 | return true; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/adapter/AnimRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.view.animation.Animation; 8 | import android.view.animation.AnimationUtils; 9 | 10 | import com.jin.gank.R; 11 | 12 | 13 | 14 | public class AnimRecyclerViewAdapter 15 | extends RecyclerView.Adapter { 16 | 17 | private static final int DELAY = 138; 18 | private int mLastPosition = -1; 19 | 20 | @Override public T onCreateViewHolder(ViewGroup parent, int viewType) { 21 | return null; 22 | } 23 | 24 | @Override public void onBindViewHolder(T holder, int position) {} 25 | 26 | @Override public int getItemCount() {return 0;} 27 | 28 | public void showItemAnim(final View view, final int position) { 29 | Context context = view.getContext(); 30 | if (position > mLastPosition) { 31 | view.setAlpha(0); 32 | view.postDelayed(() -> { 33 | Animation animation = AnimationUtils.loadAnimation(context, R.anim.slide_in_right); 34 | animation.setAnimationListener(new Animation.AnimationListener() { 35 | @Override public void onAnimationStart(Animation animation) { 36 | view.setAlpha(1); 37 | } 38 | 39 | @Override public void onAnimationEnd(Animation animation) {} 40 | 41 | @Override public void onAnimationRepeat(Animation animation) {} 42 | }); 43 | view.startAnimation(animation); 44 | }, DELAY * position); 45 | mLastPosition = position; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/adapter/GankListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.adapter; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.text.SpannableStringBuilder; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | import com.jin.gank.R; 12 | import com.jin.gank.WebActivity; 13 | import com.jin.gank.data.GankCategory; 14 | import com.jin.gank.util.StringStyleUtils; 15 | 16 | import java.util.List; 17 | 18 | import butterknife.Bind; 19 | import butterknife.ButterKnife; 20 | import butterknife.OnClick; 21 | 22 | 23 | public class GankListAdapter extends AnimRecyclerViewAdapter { 24 | 25 | private List mGankList; 26 | 27 | public GankListAdapter(List gankList) { 28 | mGankList = gankList; 29 | } 30 | 31 | @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 32 | View v = 33 | LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gank, parent, false); 34 | return new ViewHolder(v); 35 | } 36 | 37 | @Override public void onBindViewHolder(ViewHolder holder, int position) { 38 | GankCategory.ResultsEntity gank = mGankList.get(position); 39 | if (position == 0) { 40 | showCategory(holder); 41 | } 42 | else { 43 | boolean doesLastAndThis = 44 | mGankList.get(position - 1).getType().equals(mGankList.get(position).getType()); 45 | if (!doesLastAndThis) { 46 | showCategory(holder); 47 | } 48 | else if (holder.category.isShown()) holder.category.setVisibility(View.GONE); 49 | } 50 | holder.category.setText(gank.getType()); 51 | if (holder.gank.getTag() == null) { 52 | SpannableStringBuilder builder = new SpannableStringBuilder(gank.getDesc()).append( 53 | StringStyleUtils.format(holder.gank.getContext(), " (via. " + gank.getWho() + ")", 54 | R.style.ViaTextAppearance)); 55 | CharSequence gankText = builder.subSequence(0, builder.length()); 56 | holder.gank.setTag(gankText); 57 | } 58 | CharSequence text = (CharSequence) holder.gank.getTag(); 59 | holder.gank.setText(text); 60 | showItemAnim(holder.gank, position); 61 | } 62 | 63 | @Override public int getItemCount() { 64 | return mGankList.size(); 65 | } 66 | 67 | private void showCategory(ViewHolder holder) { 68 | if (!holder.category.isShown()) holder.category.setVisibility(View.VISIBLE); 69 | } 70 | 71 | class ViewHolder extends RecyclerView.ViewHolder { 72 | 73 | @Bind(R.id.tv_category) TextView category; 74 | @Bind(R.id.tv_title) TextView gank; 75 | 76 | public ViewHolder(View itemView) { 77 | super(itemView); 78 | ButterKnife.bind(this, itemView); 79 | } 80 | 81 | @OnClick(R.id.ll_gank_parent) void onGank(View v) { 82 | GankCategory.ResultsEntity gank = mGankList.get(getLayoutPosition()); 83 | Intent intent = new Intent(v.getContext(), WebActivity.class); 84 | intent.putExtra(WebActivity.EXTRA_TITLE, gank.getDesc()); 85 | intent.putExtra(WebActivity.EXTRA_URL, gank.getUrl()); 86 | v.getContext().startActivity(intent); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/application/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.application; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | /** 7 | * Created by Administrator on 2015/8/31. 8 | */ 9 | public class MyApplication extends Application { 10 | public static Context sContext; 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | sContext = this; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/data/Constant.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.data; 2 | 3 | /** 4 | * Created by Administrator on 2015/8/21. 5 | */ 6 | public class Constant { 7 | public static final String ROOT_PATH = "http://gank.io/api/"; 8 | 9 | public static final int API_COUNT = 10; 10 | 11 | public static final int VIEWPAGE_COUNT = 5; 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/data/DataBinder.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.data; 2 | 3 | import android.content.Context; 4 | import android.databinding.BindingAdapter; 5 | import android.widget.ImageView; 6 | 7 | import com.bumptech.glide.Glide; 8 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 9 | 10 | public final class DataBinder { 11 | 12 | private DataBinder() { 13 | //NO-OP 14 | } 15 | 16 | @BindingAdapter("imageUrl") 17 | public static void setImageUrl(ImageView imageView, String url) { 18 | Context context = imageView.getContext(); 19 | Glide.with(context) 20 | .load(url) 21 | .centerCrop() 22 | .diskCacheStrategy(DiskCacheStrategy.ALL) 23 | .into(imageView); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/data/GankCategory.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.data; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by Administrator on 2015/8/24. 8 | */ 9 | public class GankCategory { 10 | 11 | /** 12 | * error : false 13 | * results : [{"createdAt":"2015-08-24T03:53:33.486Z","publishedAt":"2015-08-24T04:06:07.602Z","used":true,"type":"休息视频","url":"http://v.youku.com/v_show/id_XMTMxMDA3MTEzNg==.html?from=s1.8-1-1.2","objectId":"55da953d00b04a634990f1c8","who":"代码家","desc":"从海龟鼻子里取出吸管儿(看的我要哭了","updatedAt":"2015-08-24T04:06:07.986Z"},{"createdAt":"2015-08-24T02:56:43.287Z","publishedAt":"2015-08-24T04:06:07.601Z","used":true,"type":"Android","url":"https://github.com/cesarferreira/dryrun","objectId":"55da87eb00b04a63498dee53","who":"mthli","desc":"快速尝试各种lib的demo","updatedAt":"2015-08-24T04:06:08.249Z"},{"createdAt":"2015-08-24T02:27:40.869Z","publishedAt":"2015-08-24T04:06:07.600Z","used":true,"type":"Android","url":"https://github.com/ashishb/android-security-awesome","objectId":"55da811c60b296e567e04ae4","who":"andyiac","desc":"一些android安全相关收集","updatedAt":"2015-08-24T04:06:07.992Z"},{"createdAt":"2015-08-24T02:26:06.138Z","publishedAt":"2015-08-24T04:06:07.600Z","used":true,"type":"Android","url":"https://github.com/PkmX/lcamera","objectId":"55da80be60b296e567e0364d","who":"mthli","desc":"一个使用新的Camera2 API的相机App","updatedAt":"2015-08-24T04:06:07.979Z"},{"createdAt":"2015-08-24T02:10:00.994Z","publishedAt":"2015-08-24T04:06:07.599Z","used":true,"type":"iOS","url":"https://github.com/likumb/SimpleMemo","objectId":"55da7cf800b04a63498b413f","who":"鲍永章","desc":"已上架的便签App源码,Swift编写,支持AppleWatch。","updatedAt":"2015-08-24T04:06:07.983Z"},{"createdAt":"2015-08-24T01:46:55.914Z","publishedAt":"2015-08-24T04:06:07.598Z","used":true,"type":"Android","url":"https://github.com/venmo/tooltip-view","objectId":"55da778f60b2ce30313cbcea","who":"有时放纵","desc":"一个android的工具提示view","updatedAt":"2015-08-24T04:06:07.995Z"},{"createdAt":"2015-08-24T01:37:37.406Z","publishedAt":"2015-08-24T04:06:07.597Z","used":true,"type":"Android","url":"http://zuimeia.com/app/3252/?category=15&platform=2","objectId":"55da756160b27e6c78cde002","who":"lxxself","desc":"设计师利器,安卓程序员好好学","updatedAt":"2015-08-24T04:06:07.996Z"},{"createdAt":"2015-08-24T01:39:50.174Z","publishedAt":"2015-08-24T04:06:07.597Z","used":true,"type":"Android","url":"https://github.com/hitherejoe/Android-Boilerplate","objectId":"55da75e600b05bb7664709ec","who":"有时放纵","desc":"一个用了Espresso和Robolectric的android样板","updatedAt":"2015-08-24T04:06:08.012Z"},{"createdAt":"2015-08-24T01:34:58.737Z","publishedAt":"2015-08-24T04:06:07.594Z","used":true,"type":"福利","url":"http://ww1.sinaimg.cn/large/7a8aed7bgw1evdga4dimoj20qo0hsmzf.jpg","objectId":"55da74c200b0a527148167f8","who":"张涵宇","desc":"8.24","updatedAt":"2015-08-24T04:06:08.796Z"},{"createdAt":"2015-08-23T13:18:19.102Z","publishedAt":"2015-08-24T04:06:07.593Z","used":true,"type":"拓展资源","url":"http://json.parser.online.fr/","objectId":"55d9c81b60b26a528474478e","who":"lxxself","desc":"Json Parser Online","updatedAt":"2015-08-24T04:06:08.010Z"}] 14 | */ 15 | private boolean error; 16 | private List results; 17 | 18 | public void setError(boolean error) { 19 | this.error = error; 20 | } 21 | 22 | public void setResults(List results) { 23 | this.results = results; 24 | } 25 | 26 | public boolean isError() { 27 | return error; 28 | } 29 | 30 | public List getResults() { 31 | return results; 32 | } 33 | 34 | public static class ResultsEntity implements Serializable { 35 | /** 36 | * createdAt : 2015-08-24T03:53:33.486Z 37 | * publishedAt : 2015-08-24T04:06:07.602Z 38 | * used : true 39 | * type : 休息视频 40 | * url : http://v.youku.com/v_show/id_XMTMxMDA3MTEzNg==.html?from=s1.8-1-1.2 41 | * objectId : 55da953d00b04a634990f1c8 42 | * who : 代码家 43 | * desc : 从海龟鼻子里取出吸管儿(看的我要哭了 44 | * updatedAt : 2015-08-24T04:06:07.986Z 45 | */ 46 | private String createdAt; 47 | private String publishedAt; 48 | private boolean used; 49 | private String type; 50 | private String url; 51 | private String objectId; 52 | private String who; 53 | private String desc; 54 | private String updatedAt; 55 | 56 | public void setCreatedAt(String createdAt) { 57 | this.createdAt = createdAt; 58 | } 59 | 60 | public void setPublishedAt(String publishedAt) { 61 | this.publishedAt = publishedAt; 62 | } 63 | 64 | public void setUsed(boolean used) { 65 | this.used = used; 66 | } 67 | 68 | public void setType(String type) { 69 | this.type = type; 70 | } 71 | 72 | public void setUrl(String url) { 73 | this.url = url; 74 | } 75 | 76 | public void setObjectId(String objectId) { 77 | this.objectId = objectId; 78 | } 79 | 80 | public void setWho(String who) { 81 | this.who = who; 82 | } 83 | 84 | public void setDesc(String desc) { 85 | this.desc = desc; 86 | } 87 | 88 | public void setUpdatedAt(String updatedAt) { 89 | this.updatedAt = updatedAt; 90 | } 91 | 92 | public String getCreatedAt() { 93 | return createdAt; 94 | } 95 | 96 | public String getPublishedAt() { 97 | return publishedAt; 98 | } 99 | 100 | public boolean isUsed() { 101 | return used; 102 | } 103 | 104 | public String getType() { 105 | return type; 106 | } 107 | 108 | public String getUrl() { 109 | return url; 110 | } 111 | 112 | public String getObjectId() { 113 | return objectId; 114 | } 115 | 116 | public String getWho() { 117 | return who; 118 | } 119 | 120 | public String getDesc() { 121 | return desc; 122 | } 123 | 124 | public String getUpdatedAt() { 125 | return updatedAt; 126 | } 127 | 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/data/GankDay.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.data; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by Administrator on 2015/8/24. 7 | */ 8 | public class GankDay { 9 | 10 | /** 11 | * error : false 12 | * category : ["iOS","Android","瞎推荐","拓展资源","福利","休息视频"] 13 | * results : {"福利":[{"createdAt":"2015-08-07T01:21:06.112Z","publishedAt":"2015-08-07T03:57:47.310Z","used":true,"type":"福利","url":"http://ww2.sinaimg.cn/large/7a8aed7bgw1eutscfcqtcj20dw0i0q4l.jpg","objectId":"55c4080240ac7d0a9507905e","who":"张涵宇","desc":"8.7\u2014\u2014(1)","updatedAt":"2015-08-15T03:15:54.765Z"},{"createdAt":"2015-08-07T01:21:33.518Z","publishedAt":"2015-08-07T03:57:47.229Z","used":true,"type":"福利","url":"http://ww2.sinaimg.cn/large/7a8aed7bgw1eutsd0pgiwj20go0p0djn.jpg","objectId":"55c4081d60b2c9d32a67a92e","who":"张涵宇","desc":"8.7\u2014\u2014(2)","updatedAt":"2015-08-15T03:15:54.843Z"}],"休息视频":[{"createdAt":"2015-08-06T13:06:17.211Z","publishedAt":"2015-08-07T03:57:48.104Z","used":true,"type":"休息视频","url":"http://www.zhihu.com/question/21778055/answer/19905413?utm_source=weibo&utm_medium=weibo_share&utm_content=share_answer&utm_campaign=share_button","objectId":"55c35bc960b2750766954ec3","who":"lxxself","desc":"听到就心情大好的歌,简直妖魔哈哈哈哈哈,原地址 http://v.youku.com/v_show/id_XMTQxODA5NDM2.html","updatedAt":"2015-08-15T03:15:54.872Z"}],"拓展资源":[{"createdAt":"2015-08-07T01:36:06.932Z","publishedAt":"2015-08-07T03:57:48.081Z","used":true,"type":"拓展资源","url":"https://github.com/buunguyen/octotree","objectId":"55c40b8600b08484a7f3a032","who":"lxxself","desc":"Display GitHub code in tree format","updatedAt":"2015-08-15T03:15:54.382Z"}],"瞎推荐":[{"createdAt":"2015-08-07T01:52:30.267Z","publishedAt":"2015-08-07T03:57:48.084Z","used":true,"type":"瞎推荐","url":"https://cmd.fm/","objectId":"55c40f5e00b00045334934b4","who":"lxxself","desc":"程序员的电台FmM,这个页面chrome插件有问题啊哭,我写了回删除不了啊","updatedAt":"2015-08-15T03:15:54.383Z"}],"iOS":[{"createdAt":"2015-08-07T01:32:51.588Z","publishedAt":"2015-08-07T03:57:48.070Z","used":true,"type":"iOS","url":"http://adriansampson.net/blog/llvm.html","objectId":"55c40ac360b2c9d32a67ca25","who":"CallMeWhy","desc":"LLVM 简介","updatedAt":"2015-08-15T03:15:54.384Z"},{"createdAt":"2015-08-06T14:45:18.733Z","publishedAt":"2015-08-07T03:57:47.242Z","used":true,"type":"iOS","url":"https://github.com/molon/MLLabel","objectId":"55c372fe60b2f809e41eb388","who":"鲍永章","desc":"基于TextKit的UILabel,支持超链接和自定义表达式。","updatedAt":"2015-08-15T03:15:54.536Z"},{"createdAt":"2015-08-07T01:33:30.871Z","publishedAt":"2015-08-07T03:57:48.083Z","used":true,"type":"iOS","url":"http://chris.eidhof.nl/posts/swift-c-interop.html","objectId":"55c40aea00b025867b19c9af","who":"CallMeWhy","desc":"Swift 和 C 函数","updatedAt":"2015-08-15T03:15:54.408Z"},{"createdAt":"2015-08-07T01:34:00.984Z","publishedAt":"2015-08-07T03:57:48.174Z","used":true,"type":"iOS","url":"http://airspeedvelocity.net/2015/08/03/arrays-linked-lists-and-performance/","objectId":"55c40b0800b0fac2c2809acc","who":"CallMeWhy","desc":"Arrays Linked Lists 和性能比较","updatedAt":"2015-08-15T03:15:55.105Z"}],"Android":[{"createdAt":"2015-08-06T07:15:52.065Z","publishedAt":"2015-08-07T03:57:48.045Z","used":true,"type":"Android","url":"https://github.com/recruit-lifestyle/FloatingView","objectId":"55c309a800b00045333db517","who":"mthli","desc":"类似Link Bubble的悬浮式操作设计","updatedAt":"2015-08-15T03:15:55.098Z"},{"createdAt":"2015-08-07T01:33:07.815Z","publishedAt":"2015-08-07T03:57:47.317Z","used":true,"type":"Android","url":"http://www.zhihu.com/question/33636939","objectId":"55c40ad340ac7d0a9507b324","who":"lxxself","desc":"Android开发中,有哪些让你觉得相见恨晚的方法、类或接口?","updatedAt":"2015-08-15T03:15:54.407Z"},{"createdAt":"2015-08-06T15:00:38.350Z","publishedAt":"2015-08-07T03:57:48.076Z","used":true,"type":"Android","url":"https://github.com/tom91136/Akatsuki","objectId":"55c3769660b2750766971ce6","who":"鲍永章","desc":"使用注解来处理Activity的状态恢复","updatedAt":"2015-08-15T03:15:54.866Z"},{"createdAt":"2015-08-07T02:19:44.342Z","publishedAt":"2015-08-07T03:57:48.142Z","used":true,"type":"Android","url":"https://git.oschina.net/way/PinnedHeaderListView","objectId":"55c415c060b2d140ca882eac","who":"有时放纵","desc":"Android Lollipop联系人之PinnedListView简单使用","updatedAt":"2015-08-15T03:15:55.020Z"},{"createdAt":"2015-08-06T14:58:28.171Z","publishedAt":"2015-08-07T03:57:48.073Z","used":true,"type":"Android","url":"https://github.com/Q42/AndroidScrollingImageView","objectId":"55c3761400b00045334480d2","who":"鲍永章","desc":"图片可以自动滚动的ImageView,可以实现视差效果。","updatedAt":"2015-08-15T03:15:55.123Z"}]} 14 | */ 15 | private boolean error; 16 | private List category; 17 | private ResultsEntity results; 18 | 19 | public void setError(boolean error) { 20 | this.error = error; 21 | } 22 | 23 | public void setCategory(List category) { 24 | this.category = category; 25 | } 26 | 27 | public void setResults(ResultsEntity results) { 28 | this.results = results; 29 | } 30 | 31 | public boolean isError() { 32 | return error; 33 | } 34 | 35 | public List getCategory() { 36 | return category; 37 | } 38 | 39 | public ResultsEntity getResults() { 40 | return results; 41 | } 42 | 43 | public static class ResultsEntity { 44 | /** 45 | * 福利 : [{"createdAt":"2015-08-07T01:21:06.112Z","publishedAt":"2015-08-07T03:57:47.310Z","used":true,"type":"福利","url":"http://ww2.sinaimg.cn/large/7a8aed7bgw1eutscfcqtcj20dw0i0q4l.jpg","objectId":"55c4080240ac7d0a9507905e","who":"张涵宇","desc":"8.7\u2014\u2014(1)","updatedAt":"2015-08-15T03:15:54.765Z"},{"createdAt":"2015-08-07T01:21:33.518Z","publishedAt":"2015-08-07T03:57:47.229Z","used":true,"type":"福利","url":"http://ww2.sinaimg.cn/large/7a8aed7bgw1eutsd0pgiwj20go0p0djn.jpg","objectId":"55c4081d60b2c9d32a67a92e","who":"张涵宇","desc":"8.7\u2014\u2014(2)","updatedAt":"2015-08-15T03:15:54.843Z"}] 46 | * 休息视频 : [{"createdAt":"2015-08-06T13:06:17.211Z","publishedAt":"2015-08-07T03:57:48.104Z","used":true,"type":"休息视频","url":"http://www.zhihu.com/question/21778055/answer/19905413?utm_source=weibo&utm_medium=weibo_share&utm_content=share_answer&utm_campaign=share_button","objectId":"55c35bc960b2750766954ec3","who":"lxxself","desc":"听到就心情大好的歌,简直妖魔哈哈哈哈哈,原地址 http://v.youku.com/v_show/id_XMTQxODA5NDM2.html","updatedAt":"2015-08-15T03:15:54.872Z"}] 47 | * 拓展资源 : [{"createdAt":"2015-08-07T01:36:06.932Z","publishedAt":"2015-08-07T03:57:48.081Z","used":true,"type":"拓展资源","url":"https://github.com/buunguyen/octotree","objectId":"55c40b8600b08484a7f3a032","who":"lxxself","desc":"Display GitHub code in tree format","updatedAt":"2015-08-15T03:15:54.382Z"}] 48 | * 瞎推荐 : [{"createdAt":"2015-08-07T01:52:30.267Z","publishedAt":"2015-08-07T03:57:48.084Z","used":true,"type":"瞎推荐","url":"https://cmd.fm/","objectId":"55c40f5e00b00045334934b4","who":"lxxself","desc":"程序员的电台FmM,这个页面chrome插件有问题啊哭,我写了回删除不了啊","updatedAt":"2015-08-15T03:15:54.383Z"}] 49 | * iOS : [{"createdAt":"2015-08-07T01:32:51.588Z","publishedAt":"2015-08-07T03:57:48.070Z","used":true,"type":"iOS","url":"http://adriansampson.net/blog/llvm.html","objectId":"55c40ac360b2c9d32a67ca25","who":"CallMeWhy","desc":"LLVM 简介","updatedAt":"2015-08-15T03:15:54.384Z"},{"createdAt":"2015-08-06T14:45:18.733Z","publishedAt":"2015-08-07T03:57:47.242Z","used":true,"type":"iOS","url":"https://github.com/molon/MLLabel","objectId":"55c372fe60b2f809e41eb388","who":"鲍永章","desc":"基于TextKit的UILabel,支持超链接和自定义表达式。","updatedAt":"2015-08-15T03:15:54.536Z"},{"createdAt":"2015-08-07T01:33:30.871Z","publishedAt":"2015-08-07T03:57:48.083Z","used":true,"type":"iOS","url":"http://chris.eidhof.nl/posts/swift-c-interop.html","objectId":"55c40aea00b025867b19c9af","who":"CallMeWhy","desc":"Swift 和 C 函数","updatedAt":"2015-08-15T03:15:54.408Z"},{"createdAt":"2015-08-07T01:34:00.984Z","publishedAt":"2015-08-07T03:57:48.174Z","used":true,"type":"iOS","url":"http://airspeedvelocity.net/2015/08/03/arrays-linked-lists-and-performance/","objectId":"55c40b0800b0fac2c2809acc","who":"CallMeWhy","desc":"Arrays Linked Lists 和性能比较","updatedAt":"2015-08-15T03:15:55.105Z"}] 50 | * Android : [{"createdAt":"2015-08-06T07:15:52.065Z","publishedAt":"2015-08-07T03:57:48.045Z","used":true,"type":"Android","url":"https://github.com/recruit-lifestyle/FloatingView","objectId":"55c309a800b00045333db517","who":"mthli","desc":"类似Link Bubble的悬浮式操作设计","updatedAt":"2015-08-15T03:15:55.098Z"},{"createdAt":"2015-08-07T01:33:07.815Z","publishedAt":"2015-08-07T03:57:47.317Z","used":true,"type":"Android","url":"http://www.zhihu.com/question/33636939","objectId":"55c40ad340ac7d0a9507b324","who":"lxxself","desc":"Android开发中,有哪些让你觉得相见恨晚的方法、类或接口?","updatedAt":"2015-08-15T03:15:54.407Z"},{"createdAt":"2015-08-06T15:00:38.350Z","publishedAt":"2015-08-07T03:57:48.076Z","used":true,"type":"Android","url":"https://github.com/tom91136/Akatsuki","objectId":"55c3769660b2750766971ce6","who":"鲍永章","desc":"使用注解来处理Activity的状态恢复","updatedAt":"2015-08-15T03:15:54.866Z"},{"createdAt":"2015-08-07T02:19:44.342Z","publishedAt":"2015-08-07T03:57:48.142Z","used":true,"type":"Android","url":"https://git.oschina.net/way/PinnedHeaderListView","objectId":"55c415c060b2d140ca882eac","who":"有时放纵","desc":"Android Lollipop联系人之PinnedListView简单使用","updatedAt":"2015-08-15T03:15:55.020Z"},{"createdAt":"2015-08-06T14:58:28.171Z","publishedAt":"2015-08-07T03:57:48.073Z","used":true,"type":"Android","url":"https://github.com/Q42/AndroidScrollingImageView","objectId":"55c3761400b00045334480d2","who":"鲍永章","desc":"图片可以自动滚动的ImageView,可以实现视差效果。","updatedAt":"2015-08-15T03:15:55.123Z"}] 51 | */ 52 | private List 福利; 53 | private List 休息视频; 54 | private List 拓展资源; 55 | private List 瞎推荐; 56 | private List iOS; 57 | private List Android; 58 | 59 | public void set福利(List 福利) { 60 | this.福利 = 福利; 61 | } 62 | 63 | public void set休息视频(List 休息视频) { 64 | this.休息视频 = 休息视频; 65 | } 66 | 67 | public void set拓展资源(List 拓展资源) { 68 | this.拓展资源 = 拓展资源; 69 | } 70 | 71 | public void set瞎推荐(List 瞎推荐) { 72 | this.瞎推荐 = 瞎推荐; 73 | } 74 | 75 | public void setIOS(List iOS) { 76 | this.iOS = iOS; 77 | } 78 | 79 | public void setAndroid(List Android) { 80 | this.Android = Android; 81 | } 82 | 83 | public List get福利() { 84 | return 福利; 85 | } 86 | 87 | public List get休息视频() { 88 | return 休息视频; 89 | } 90 | 91 | public List get拓展资源() { 92 | return 拓展资源; 93 | } 94 | 95 | public List get瞎推荐() { 96 | return 瞎推荐; 97 | } 98 | 99 | public List getIOS() { 100 | return iOS; 101 | } 102 | 103 | public List getAndroid() { 104 | return Android; 105 | } 106 | 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/data/GankRandom.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.data; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by Administrator on 2015/8/24. 7 | */ 8 | public class GankRandom { 9 | 10 | /** 11 | * error : false 12 | * results : [{"createdAt":"2015-05-28T11:18:58.168Z","publishedAt":"2015-05-29T03:36:51.888Z","used":true,"type":"Android","url":"https://github.com/romannurik/AndroidSDKSearchExtension","objectId":"5566f9a2e4b0aec39bf9b10b","who":"mthli","desc":"快速搜索Android SDK文档的Chrome扩展","updatedAt":"2015-08-15T03:09:57.714Z"},{"createdAt":"2015-08-18T15:24:36.747Z","publishedAt":"2015-08-19T03:56:32.551Z","used":true,"type":"Android","url":"https://github.com/SkillCollege/SimplifyReader","objectId":"55d34e3400b0de09f8fb0532","who":"Jason","desc":"一款基于Google Material Design设计开发的Android客户端,包括新闻简读,图片浏览,视频爽看 ,音乐轻听以及二维码扫描五个子模块,项目采取的是MVP架构开发。","updatedAt":"2015-08-19T03:56:34.075Z"},{"createdAt":"2015-06-07T11:00:27.934Z","publishedAt":"2015-06-09T03:30:27.469Z","used":true,"type":"Android","url":"https://github.com/JorgeCastilloPrz/FABProgressCircle","objectId":"5574244be4b029ae5fd0f298","who":"mthli","desc":"在FloatingActionButton上显示环形进度条","updatedAt":"2015-08-15T03:09:57.923Z"},{"createdAt":"2015-05-18T02:25:10.936Z","publishedAt":"2015-05-19T08:40:35.957Z","used":true,"type":"Android","url":"https://github.com/dmytrodanylyk/circular-progress-button","objectId":"55594d86e4b0b8cd3b7aefcc","who":"Jason","desc":"带进度显示的Button","updatedAt":"2015-08-15T03:09:57.704Z"},{"createdAt":"2015-06-02T15:38:30.156Z","publishedAt":"2015-06-04T05:01:40.057Z","used":true,"type":"Android","url":"https://github.com/yaa110/Piclice","objectId":"556dcdf6e4b005426cf9e94d","who":"Jason","desc":"一款将图片切片与分享的应用","updatedAt":"2015-08-15T03:09:57.802Z"},{"createdAt":"2015-07-13T02:02:11.196Z","publishedAt":"2015-07-13T03:33:29.420Z","used":true,"type":"Android","url":"https://github.com/worker8/TourGuide","objectId":"55a31c23e4b031b0057bb3ff","who":"有时放纵","desc":"一个用于你的android app引导功能的库","updatedAt":"2015-08-15T03:09:59.286Z"},{"createdAt":"2015-07-27T04:51:14.289Z","publishedAt":"2015-07-29T03:32:59.575Z","used":true,"type":"Android","url":"https://github.com/code-mc/material-icon-lib","objectId":"55b5b8c200b0c43f5b1de3c8","who":"Jason","desc":"包含1000多个material矢量图标的安卓图标库","updatedAt":"2015-08-15T03:15:54.746Z"},{"createdAt":"2015-06-05T04:24:39.142Z","publishedAt":"2015-06-09T03:38:06.063Z","used":true,"type":"Android","url":"https://github.com/PaoloRotolo/AppIntro","objectId":"55712487e4b0ba4266fa223e","who":"mthli","desc":"一个漂亮的App功能介绍库","updatedAt":"2015-08-15T03:09:57.927Z"},{"createdAt":"2015-08-10T22:47:04.910Z","publishedAt":"2015-08-11T03:28:03.108Z","used":true,"type":"Android","url":"https://github.com/kingideayou/SlideBottomPanel","objectId":"55c929e860b20fc9a88711bf","who":"代码家","desc":"底部划出菜单,滑动时背景图透明度渐变,支持嵌套 LiewView 或 ScrollView。","updatedAt":"2015-08-15T03:15:54.387Z"},{"createdAt":"2015-06-16T16:00:11.974Z","publishedAt":"2015-06-17T04:09:49.084Z","used":true,"type":"Android","url":"http://blog.csdn.net/garybook/article/details/7925728","objectId":"5580480be4b02f99238c9596","who":"LHF","desc":"Android中的签名机制","updatedAt":"2015-08-15T03:09:57.814Z"}] 13 | */ 14 | private boolean error; 15 | private List results; 16 | 17 | public void setError(boolean error) { 18 | this.error = error; 19 | } 20 | 21 | public void setResults(List results) { 22 | this.results = results; 23 | } 24 | 25 | public boolean isError() { 26 | return error; 27 | } 28 | 29 | public List getResults() { 30 | return results; 31 | } 32 | 33 | public static class ResultsEntity { 34 | /** 35 | * createdAt : 2015-05-28T11:18:58.168Z 36 | * publishedAt : 2015-05-29T03:36:51.888Z 37 | * used : true 38 | * type : Android 39 | * url : https://github.com/romannurik/AndroidSDKSearchExtension 40 | * objectId : 5566f9a2e4b0aec39bf9b10b 41 | * who : mthli 42 | * desc : 快速搜索Android SDK文档的Chrome扩展 43 | * updatedAt : 2015-08-15T03:09:57.714Z 44 | */ 45 | private String createdAt; 46 | private String publishedAt; 47 | private boolean used; 48 | private String type; 49 | private String url; 50 | private String objectId; 51 | private String who; 52 | private String desc; 53 | private String updatedAt; 54 | 55 | public void setCreatedAt(String createdAt) { 56 | this.createdAt = createdAt; 57 | } 58 | 59 | public void setPublishedAt(String publishedAt) { 60 | this.publishedAt = publishedAt; 61 | } 62 | 63 | public void setUsed(boolean used) { 64 | this.used = used; 65 | } 66 | 67 | public void setType(String type) { 68 | this.type = type; 69 | } 70 | 71 | public void setUrl(String url) { 72 | this.url = url; 73 | } 74 | 75 | public void setObjectId(String objectId) { 76 | this.objectId = objectId; 77 | } 78 | 79 | public void setWho(String who) { 80 | this.who = who; 81 | } 82 | 83 | public void setDesc(String desc) { 84 | this.desc = desc; 85 | } 86 | 87 | public void setUpdatedAt(String updatedAt) { 88 | this.updatedAt = updatedAt; 89 | } 90 | 91 | public String getCreatedAt() { 92 | return createdAt; 93 | } 94 | 95 | public String getPublishedAt() { 96 | return publishedAt; 97 | } 98 | 99 | public boolean isUsed() { 100 | return used; 101 | } 102 | 103 | public String getType() { 104 | return type; 105 | } 106 | 107 | public String getUrl() { 108 | return url; 109 | } 110 | 111 | public String getObjectId() { 112 | return objectId; 113 | } 114 | 115 | public String getWho() { 116 | return who; 117 | } 118 | 119 | public String getDesc() { 120 | return desc; 121 | } 122 | 123 | public String getUpdatedAt() { 124 | return updatedAt; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/decoration/DividerGridItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.decoration; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Rect; 6 | import android.graphics.drawable.Drawable; 7 | import android.support.v7.widget.GridLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.support.v7.widget.RecyclerView.LayoutManager; 10 | import android.support.v7.widget.RecyclerView.State; 11 | import android.support.v7.widget.StaggeredGridLayoutManager; 12 | import android.view.View; 13 | 14 | import com.jin.gank.R; 15 | 16 | 17 | 18 | public class DividerGridItemDecoration extends RecyclerView.ItemDecoration { 19 | private Drawable mDivider; 20 | public DividerGridItemDecoration(Context context) { 21 | mDivider = context.getResources().getDrawable(R.drawable.gride_divider); 22 | 23 | } 24 | 25 | @Override 26 | public void onDraw(Canvas c, RecyclerView parent, State state) { 27 | drawHorizontal(c, parent); 28 | drawVertical(c, parent); 29 | } 30 | 31 | private int getSpanCount(RecyclerView parent) { 32 | // 列数 33 | int spanCount = -1; 34 | LayoutManager layoutManager = parent.getLayoutManager(); 35 | if (layoutManager instanceof GridLayoutManager) { 36 | 37 | spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); 38 | } else if (layoutManager instanceof StaggeredGridLayoutManager) { 39 | spanCount = ((StaggeredGridLayoutManager) layoutManager) 40 | .getSpanCount(); 41 | } 42 | return spanCount; 43 | } 44 | 45 | public void drawHorizontal(Canvas c, RecyclerView parent) { 46 | int childCount = parent.getChildCount(); 47 | for (int i = 0; i < childCount; i++) { 48 | final View child = parent.getChildAt(i); 49 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 50 | .getLayoutParams(); 51 | final int left = child.getLeft() - params.leftMargin; 52 | final int right = child.getRight() + params.rightMargin 53 | + mDivider.getIntrinsicWidth(); 54 | final int top = child.getBottom() + params.bottomMargin; 55 | final int bottom = top + mDivider.getIntrinsicHeight(); 56 | mDivider.setBounds(left, top, right, bottom); 57 | mDivider.draw(c); 58 | } 59 | } 60 | 61 | public void drawVertical(Canvas c, RecyclerView parent) { 62 | int childCount = parent.getChildCount(); 63 | for (int i = 0; i < childCount; i++) { 64 | final View child = parent.getChildAt(i); 65 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 66 | .getLayoutParams(); 67 | final int top = child.getTop() - params.topMargin; 68 | final int bottom = child.getBottom() + params.bottomMargin; 69 | final int left = child.getRight() + params.rightMargin; 70 | final int right = left + mDivider.getIntrinsicWidth(); 71 | mDivider.setBounds(left, top, right, bottom); 72 | mDivider.draw(c); 73 | } 74 | } 75 | 76 | private boolean isLastColum(RecyclerView parent, int pos, int spanCount, 77 | int childCount) { 78 | LayoutManager layoutManager = parent.getLayoutManager(); 79 | if (layoutManager instanceof GridLayoutManager) { 80 | if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 81 | { 82 | return true; 83 | } 84 | } else if (layoutManager instanceof StaggeredGridLayoutManager) { 85 | int orientation = ((StaggeredGridLayoutManager) layoutManager) 86 | .getOrientation(); 87 | if (orientation == StaggeredGridLayoutManager.VERTICAL) { 88 | if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 89 | { 90 | return true; 91 | } 92 | } else { 93 | childCount = childCount - childCount % spanCount; 94 | if (pos >= childCount)// 如果是最后一列,则不需要绘制右边 95 | return true; 96 | } 97 | } 98 | return false; 99 | } 100 | 101 | private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, 102 | int childCount) { 103 | LayoutManager layoutManager = parent.getLayoutManager(); 104 | if (layoutManager instanceof GridLayoutManager) { 105 | childCount = childCount - childCount % spanCount; 106 | if (pos >= childCount)// 如果是最后一行,则不需要绘制底部 107 | return true; 108 | } else if (layoutManager instanceof StaggeredGridLayoutManager) { 109 | int orientation = ((StaggeredGridLayoutManager) layoutManager) 110 | .getOrientation(); 111 | // StaggeredGridLayoutManager 且纵向滚动 112 | if (orientation == StaggeredGridLayoutManager.VERTICAL) { 113 | childCount = childCount - childCount % spanCount; 114 | // 如果是最后一行,则不需要绘制底部 115 | if (pos >= childCount) 116 | return true; 117 | } else 118 | // StaggeredGridLayoutManager 且横向滚动 119 | { 120 | // 如果是最后一行,则不需要绘制底部 121 | if ((pos + 1) % spanCount == 0) { 122 | return true; 123 | } 124 | } 125 | } 126 | return false; 127 | } 128 | 129 | 130 | @Override 131 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { 132 | super.getItemOffsets(outRect, view, parent, state); 133 | int itemPosition = parent.getChildAdapterPosition(view); 134 | int spanCount = getSpanCount(parent); 135 | int childCount = parent.getAdapter().getItemCount(); 136 | if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部 137 | { 138 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 139 | } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边 140 | { 141 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 142 | } else { 143 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 144 | mDivider.getIntrinsicHeight()); 145 | } 146 | 147 | } 148 | } 149 | 150 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/decoration/DividerLinearItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.decoration; 2 | /* 3 | * Copyright (C) 2014 The Android Open Source Project 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * limitations under the License. 7 | */ 8 | 9 | import android.content.Context; 10 | import android.graphics.Canvas; 11 | import android.graphics.Rect; 12 | import android.graphics.drawable.Drawable; 13 | import android.support.v7.widget.LinearLayoutManager; 14 | import android.support.v7.widget.RecyclerView; 15 | import android.view.View; 16 | 17 | import com.jin.gank.R; 18 | 19 | 20 | 21 | public class DividerLinearItemDecoration extends RecyclerView.ItemDecoration { 22 | private static final String TAG = "DividerItemDecoration"; 23 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; 24 | 25 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; 26 | 27 | private Drawable mDivider; 28 | 29 | private int mOrientation; 30 | 31 | public DividerLinearItemDecoration(Context context, int orientation) { 32 | mDivider = context.getResources().getDrawable(R.drawable.line_divider); 33 | setOrientation(orientation); 34 | } 35 | 36 | public void setOrientation(int orientation) { 37 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { 38 | throw new IllegalArgumentException("invalid orientation"); 39 | } 40 | mOrientation = orientation; 41 | } 42 | 43 | 44 | public void drawVertical(Canvas c, RecyclerView parent) { 45 | final int left = parent.getPaddingLeft(); 46 | final int right = parent.getWidth() - parent.getPaddingRight(); 47 | 48 | final int childCount = parent.getChildCount(); 49 | for (int i = 0; i < childCount; i++) { 50 | final View child = parent.getChildAt(i); 51 | android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); 52 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 53 | .getLayoutParams(); 54 | final int top = child.getBottom() + params.bottomMargin; 55 | final int bottom = top + mDivider.getIntrinsicHeight(); 56 | mDivider.setBounds(left, top, right, bottom); 57 | mDivider.draw(c); 58 | } 59 | } 60 | 61 | public void drawHorizontal(Canvas c, RecyclerView parent) { 62 | final int top = parent.getPaddingTop(); 63 | final int bottom = parent.getHeight() - parent.getPaddingBottom(); 64 | 65 | final int childCount = parent.getChildCount(); 66 | for (int i = 0; i < childCount; i++) { 67 | final View child = parent.getChildAt(i); 68 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 69 | .getLayoutParams(); 70 | final int left = child.getRight() + params.rightMargin; 71 | final int right = left + mDivider.getIntrinsicHeight(); 72 | mDivider.setBounds(left, top, right, bottom); 73 | mDivider.draw(c); 74 | } 75 | } 76 | 77 | 78 | @Override 79 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 80 | super.onDraw(c, parent, state); 81 | if (mOrientation == VERTICAL_LIST) { 82 | drawVertical(c, parent); 83 | } else { 84 | drawHorizontal(c, parent); 85 | } 86 | } 87 | 88 | @Override 89 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 90 | super.onDrawOver(c, parent, state); 91 | } 92 | 93 | @Override 94 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 95 | super.getItemOffsets(outRect, view, parent, state); 96 | if (mOrientation == VERTICAL_LIST) { 97 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 98 | } else { 99 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/fragment/GankFragment.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.fragment; 2 | 3 | import android.content.Intent; 4 | import android.content.pm.ActivityInfo; 5 | import android.content.res.Configuration; 6 | import android.os.Bundle; 7 | import android.support.design.widget.CoordinatorLayout; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.view.ViewStub; 17 | 18 | import com.bumptech.glide.Glide; 19 | import com.jin.gank.R; 20 | import com.jin.gank.WebActivity; 21 | import com.jin.gank.adapter.GankListAdapter; 22 | import com.jin.gank.data.GankCategory; 23 | import com.jin.gank.data.GankDay; 24 | import com.jin.gank.network.RetrofitHelp; 25 | import com.jin.gank.util.Once; 26 | import com.jin.gank.util.ShareUtils; 27 | import com.jin.gank.util.ToastUtils; 28 | import com.jin.gank.view.GoodAppBarLayout; 29 | import com.jin.gank.view.LoveVideoView; 30 | import com.jin.gank.view.VideoImageView; 31 | 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | 35 | import butterknife.Bind; 36 | import butterknife.ButterKnife; 37 | import butterknife.OnClick; 38 | import rx.Subscription; 39 | import rx.android.schedulers.AndroidSchedulers; 40 | import rx.schedulers.Schedulers; 41 | 42 | 43 | public class GankFragment extends Fragment { 44 | 45 | private final String TAG = "GankFragment"; 46 | private static final String ARG_YEAR = "year"; 47 | private static final String ARG_MONTH = "month"; 48 | private static final String ARG_DAY = "day"; 49 | 50 | @Bind(R.id.rv_gank) 51 | RecyclerView mRecyclerView; 52 | @Bind(R.id.stub_empty_view) 53 | ViewStub mEmptyViewStub; 54 | @Bind(R.id.stub_video_view) 55 | ViewStub mVideoViewStub; 56 | @Bind(R.id.iv_video) 57 | VideoImageView mVideoImageView; 58 | @Bind(R.id.header_appbar) 59 | GoodAppBarLayout mAppBarLayout; 60 | @Bind(R.id.cl_content) 61 | CoordinatorLayout mLayout; 62 | LoveVideoView mVideoView; 63 | 64 | int mYear, mMonth, mDay; 65 | List mGankList; 66 | String mVideoPreviewUrl; 67 | boolean mIsVideoViewInflated = false; 68 | Subscription mSubscription; 69 | GankListAdapter mAdapter; 70 | 71 | /** 72 | * Returns a new instance of this fragment for the given section 73 | * number. 74 | */ 75 | public static GankFragment newInstance(int year, int month, int day) { 76 | GankFragment fragment = new GankFragment(); 77 | Bundle args = new Bundle(); 78 | args.putInt(ARG_YEAR, year); 79 | args.putInt(ARG_MONTH, month); 80 | args.putInt(ARG_DAY, day); 81 | fragment.setArguments(args); 82 | return fragment; 83 | } 84 | 85 | public GankFragment() { 86 | 87 | } 88 | 89 | @Override 90 | public void onCreate(Bundle savedInstanceState) { 91 | super.onCreate(savedInstanceState); 92 | mGankList = new ArrayList<>(); 93 | mAdapter = new GankListAdapter(mGankList); 94 | parseArguments(); 95 | setRetainInstance(true); 96 | setHasOptionsMenu(true); 97 | } 98 | 99 | private void parseArguments() { 100 | Bundle bundle = getArguments(); 101 | mYear = bundle.getInt(ARG_YEAR); 102 | mMonth = bundle.getInt(ARG_MONTH); 103 | mDay = bundle.getInt(ARG_DAY); 104 | } 105 | 106 | @Override 107 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 108 | Bundle savedInstanceState) { 109 | View rootView = inflater.inflate(R.layout.fragment_gank, container, false); 110 | ButterKnife.bind(this, rootView); 111 | initRecyclerView(); 112 | setVideoViewPosition(getResources().getConfiguration()); 113 | return rootView; 114 | } 115 | 116 | @Override 117 | public void onViewCreated(View view, Bundle savedInstanceState) { 118 | super.onViewCreated(view, savedInstanceState); 119 | if (mGankList.size() == 0) getData(); 120 | if (mVideoPreviewUrl != null) { 121 | Glide.with(this).load(mVideoPreviewUrl).into(mVideoImageView); 122 | } 123 | } 124 | 125 | private void initRecyclerView() { 126 | LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 127 | mRecyclerView.setLayoutManager(layoutManager); 128 | mRecyclerView.setAdapter(mAdapter); 129 | } 130 | 131 | private void getData() { 132 | 133 | mSubscription = RetrofitHelp.getApi().listGankDay(mYear, mMonth, mDay) 134 | .subscribeOn(Schedulers.newThread()) 135 | .observeOn(AndroidSchedulers.mainThread()) 136 | .map(data -> data.getResults()) 137 | .map(this::addAllResults) 138 | .subscribe(list -> { 139 | if (list.isEmpty()) { 140 | showEmptyView(); 141 | } else { 142 | mAdapter.notifyDataSetChanged(); 143 | } 144 | }, e -> { 145 | Log.e(TAG, e.getMessage()); 146 | }); 147 | getAndParseVideoPreview(); 148 | 149 | } 150 | 151 | private void getAndParseVideoPreview() { 152 | mVideoImageView.post(() -> Glide.with(mVideoImageView.getContext()) 153 | .load(mVideoPreviewUrl) 154 | .into(mVideoImageView)); 155 | 156 | 157 | /*OkHttpClient client = new OkHttpClient(); 158 | String url = getString(R.string.url_gank_io) + String.format("%s/%s/%s", mYear, mMonth, mDay); 159 | Request request = new Request.Builder().url(url).build(); 160 | client.newCall(request).enqueue(new Callback() { 161 | @Override 162 | public void onFailure(Call call, IOException e) { 163 | ToastUtils.showShort(e.getMessage()); 164 | } 165 | 166 | @Override 167 | public void onResponse(Call call, Response response) throws IOException { 168 | String body = response.body().string(); 169 | mVideoPreviewUrl = LoveStringUtils.getVideoPreviewImageUrl(body); 170 | if (mVideoPreviewUrl != null && mVideoImageView != null) { 171 | mVideoImageView.post(() -> Glide.with(mVideoImageView.getContext()) 172 | .load(mVideoPreviewUrl) 173 | .into(mVideoImageView)); 174 | } 175 | } 176 | });*/ 177 | } 178 | 179 | private void showEmptyView() { 180 | mEmptyViewStub.inflate(); 181 | } 182 | 183 | private List addAllResults(GankDay.ResultsEntity results) { 184 | //初始化视频图片 185 | if (results.get福利() != null) { 186 | mVideoImageView.post(() -> Glide.with(mVideoImageView.getContext()) 187 | .load(results.get福利().get(0).getUrl()) 188 | .into(mVideoImageView)); 189 | } 190 | 191 | if (results.getAndroid() != null) mGankList.addAll(results.getAndroid()); 192 | if (results.getIOS() != null) mGankList.addAll(results.getIOS()); 193 | if (results.get拓展资源() != null) mGankList.addAll(results.get拓展资源()); 194 | if (results.get瞎推荐() != null) mGankList.addAll(results.get瞎推荐()); 195 | if (results.get休息视频() != null) mGankList.addAll(0, results.get休息视频()); 196 | return mGankList; 197 | } 198 | 199 | @OnClick(R.id.header_appbar) 200 | void onPlayVideo() { 201 | getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 202 | } 203 | 204 | private void setVideoViewPosition(Configuration newConfig) { 205 | switch (newConfig.orientation) { 206 | case Configuration.ORIENTATION_LANDSCAPE: { 207 | if (mIsVideoViewInflated) { 208 | mVideoViewStub.setVisibility(View.VISIBLE); 209 | } else { 210 | mVideoView = (LoveVideoView) mVideoViewStub.inflate(); 211 | mIsVideoViewInflated = true; 212 | String tip = getString(R.string.tip_video_play); 213 | new Once(mVideoView.getContext()).show(tip, () -> ToastUtils.showLongLongLong(tip)); 214 | } 215 | if (mGankList.size() > 0 && mGankList.get(0).getType().equals("休息视频")) { 216 | mVideoView.loadUrl(mGankList.get(0).getUrl()); 217 | } 218 | break; 219 | } 220 | case Configuration.ORIENTATION_PORTRAIT: 221 | case Configuration.ORIENTATION_UNDEFINED: 222 | default: { 223 | mVideoViewStub.setVisibility(View.GONE); 224 | break; 225 | } 226 | } 227 | } 228 | 229 | void closePlayer() { 230 | getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 231 | ToastUtils.showShort(getString(R.string.daimajia_has_gone)); 232 | } 233 | 234 | @Override 235 | public void onConfigurationChanged(Configuration newConfig) { 236 | setVideoViewPosition(newConfig); 237 | super.onConfigurationChanged(newConfig); 238 | } 239 | 240 | /*@Subscribe public void onKeyBackClick(OnKeyBackClickEvent event) { 241 | if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 242 | getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 243 | } 244 | }*/ 245 | 246 | @Override 247 | public boolean onOptionsItemSelected(MenuItem item) { 248 | int id = item.getItemId(); 249 | switch (id) { 250 | case R.id.action_share: 251 | if (mGankList.size() != 0) { 252 | GankCategory.ResultsEntity gank = mGankList.get(0); 253 | String shareText = gank.getDesc() + gank.getUrl() + getString(R.string.share_from); 254 | ShareUtils.share(getActivity(), shareText); 255 | } else { 256 | ShareUtils.share(getActivity()); 257 | } 258 | return true; 259 | 260 | case R.id.action_subject: 261 | openTodaySubject(); 262 | return true; 263 | } 264 | return super.onOptionsItemSelected(item); 265 | } 266 | 267 | private void openTodaySubject() { 268 | String url = getString(R.string.url_gank_io) + String.format("%s/%s/%s", mYear, mMonth, mDay); 269 | Intent intent = new Intent(getActivity(), WebActivity.class); 270 | intent.putExtra(WebActivity.EXTRA_URL, url); 271 | intent.putExtra(WebActivity.EXTRA_TITLE, getString(R.string.action_subject)); 272 | startActivity(intent); 273 | } 274 | 275 | @Override 276 | public void onResume() { 277 | super.onResume(); 278 | if (mVideoView != null) mVideoView.resumeTimers(); 279 | } 280 | 281 | @Override 282 | public void onPause() { 283 | super.onPause(); 284 | if (mVideoView != null) mVideoView.pauseTimers(); 285 | } 286 | 287 | @Override 288 | public void onDestroyView() { 289 | super.onDestroyView(); 290 | ButterKnife.unbind(this); 291 | } 292 | 293 | @Override 294 | public void onDestroy() { 295 | super.onDestroy(); 296 | if (mSubscription != null) mSubscription.unsubscribe(); 297 | if (mVideoView != null) mVideoView.destroy(); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/fragment/MyFragment.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.fragment; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.widget.SwipeRefreshLayout; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import com.jin.gank.R; 15 | import com.jin.gank.data.Constant; 16 | import com.jin.gank.data.GankCategory; 17 | import com.jin.gank.databinding.ItemCategoryBinding; 18 | import com.jin.gank.network.RetrofitHelp; 19 | import com.malinskiy.superrecyclerview.OnMoreListener; 20 | import com.malinskiy.superrecyclerview.SuperRecyclerView; 21 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItem; 22 | import com.vlonjatg.progressactivity.ProgressActivity; 23 | 24 | import java.util.List; 25 | 26 | import butterknife.Bind; 27 | import butterknife.ButterKnife; 28 | import jp.wasabeef.recyclerview.animators.OvershootInLeftAnimator; 29 | import rx.Observable; 30 | import rx.android.schedulers.AndroidSchedulers; 31 | import rx.schedulers.Schedulers; 32 | 33 | /** 34 | * Created by Administrator on 2015/8/25. 35 | */ 36 | public class MyFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener, OnMoreListener { 37 | private static final String TAG = "MyFragment"; 38 | @Bind(R.id.progressActivity) 39 | ProgressActivity mProgressActivity; 40 | @Bind(R.id.superRecyclerView_category) 41 | SuperRecyclerView mSuperRecyclerViewCategory; 42 | 43 | 44 | private List mCategorys; 45 | private String[] mCategoryArray; 46 | private LayoutInflater mInflater; 47 | private MyAdapter mAdapter; 48 | private boolean isLoadMore = true; 49 | private int position; 50 | 51 | @Override 52 | public void onCreate(@Nullable Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | mInflater = LayoutInflater.from(getActivity()); 55 | mCategoryArray = getResources().getStringArray(R.array.category_list); 56 | } 57 | 58 | @Override 59 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 60 | super.onViewCreated(view, savedInstanceState); 61 | position = FragmentPagerItem.getPosition(getArguments()); 62 | 63 | mSuperRecyclerViewCategory.setLayoutManager(new LinearLayoutManager(getActivity())); 64 | mSuperRecyclerViewCategory.getRecyclerView().setItemAnimator(new OvershootInLeftAnimator()); 65 | mSuperRecyclerViewCategory.setRefreshListener(this); 66 | mSuperRecyclerViewCategory.setupMoreListener(this, 10); 67 | mSuperRecyclerViewCategory.setRefreshingColorResources(android.R.color.holo_orange_light, android.R.color.holo_blue_light, android.R.color.holo_green_light, android.R.color.holo_red_light); 68 | if (mCategorys == null || mCategorys.size() == 0) { 69 | loadCategoryData(mCategoryArray[position], Constant.API_COUNT, 1, true); 70 | } else { 71 | mAdapter.notifyDataSetChanged(); 72 | } 73 | 74 | } 75 | 76 | //下载数据信息 77 | private void loadCategoryData(String category, int count, int page, boolean firstLoad) { 78 | if (firstLoad) { 79 | mProgressActivity.showLoading(); 80 | if (mCategorys != null) 81 | mCategorys.clear(); 82 | } 83 | 84 | RetrofitHelp.getApi().listGankCategory(category, count, page) 85 | .subscribeOn(Schedulers.io()) 86 | .observeOn(AndroidSchedulers.mainThread()) 87 | .flatMap(Categorys -> { 88 | //判断是否已经加载到最后一页 89 | if (Categorys.getResults().size() < 10) 90 | isLoadMore = false; 91 | return Observable.just(Categorys.getResults()); 92 | }) 93 | .subscribe(Categorys -> { 94 | mProgressActivity.showContent(); 95 | if (mAdapter == null) { 96 | mCategorys = Categorys; 97 | mAdapter = new MyAdapter(Categorys); 98 | mSuperRecyclerViewCategory.setAdapter(mAdapter); 99 | } else { 100 | if (!firstLoad) 101 | mCategorys.addAll(mCategorys.size(), Categorys); 102 | 103 | mAdapter.setCategorys(mCategorys); 104 | mAdapter.notifyDataSetChanged(); 105 | } 106 | 107 | mSuperRecyclerViewCategory.hideMoreProgress(); 108 | mSuperRecyclerViewCategory.getSwipeToRefresh().setRefreshing(false); 109 | }, 110 | err -> mProgressActivity.showError(null, "错误", err.toString(), "重试", v -> loadCategoryData(category, Constant.API_COUNT, 1, true))); 111 | } 112 | 113 | @Override 114 | public void onMoreAsked(int numberOfItems, int numberBeforeMore, int currentItemPos) { 115 | if (isLoadMore) { 116 | loadCategoryData(mCategoryArray[position], Constant.API_COUNT, numberOfItems / Constant.API_COUNT + 1, false); 117 | } else { 118 | mSuperRecyclerViewCategory.hideMoreProgress(); 119 | } 120 | } 121 | 122 | @Override 123 | public void onRefresh() { 124 | isLoadMore = true; 125 | loadCategoryData(mCategoryArray[position], Constant.API_COUNT, 1, true); 126 | } 127 | 128 | 129 | private class MyAdapter extends RecyclerView.Adapter { 130 | 131 | public void setCategorys(List Categorys) { 132 | this.Categorys = Categorys; 133 | } 134 | 135 | private List Categorys; 136 | 137 | public MyAdapter(List Categorys) { 138 | super(); 139 | this.Categorys = Categorys; 140 | } 141 | 142 | @Override 143 | public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 144 | View contentView = mInflater.inflate(R.layout.item_category, parent, false); 145 | MyViewHolder holder = new MyViewHolder(contentView); 146 | return holder; 147 | } 148 | 149 | @Override 150 | public void onBindViewHolder(MyViewHolder holder, final int position) { 151 | GankCategory.ResultsEntity Category = Categorys.get(position); 152 | holder.bind(Category); 153 | } 154 | 155 | @Override 156 | public int getItemCount() { 157 | return Categorys.size(); 158 | } 159 | 160 | 161 | class MyViewHolder extends RecyclerView.ViewHolder { 162 | private ItemCategoryBinding mCategoryBinding; 163 | 164 | public MyViewHolder(View convertView) { 165 | super(convertView); 166 | mCategoryBinding = DataBindingUtil.bind(convertView); 167 | 168 | } 169 | 170 | public void bind(GankCategory.ResultsEntity Category) { 171 | mCategoryBinding.setCategory(Category); 172 | } 173 | } 174 | 175 | } 176 | 177 | 178 | @Nullable 179 | @Override 180 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 181 | container = (ViewGroup) inflater.inflate(R.layout.fragment_category, null, false); 182 | ButterKnife.bind(this, container); 183 | return container; 184 | } 185 | 186 | @Override 187 | public void onDestroyView() { 188 | super.onDestroyView(); 189 | ButterKnife.unbind(this); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/fragment/MyImgFragment.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.fragment; 2 | 3 | import android.content.Intent; 4 | import android.databinding.DataBindingUtil; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.ActivityCompat; 8 | import android.support.v4.app.ActivityOptionsCompat; 9 | import android.support.v4.app.Fragment; 10 | import android.support.v4.widget.SwipeRefreshLayout; 11 | import android.support.v7.widget.RecyclerView; 12 | import android.support.v7.widget.StaggeredGridLayoutManager; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | 17 | import com.jin.gank.GankActivity; 18 | import com.jin.gank.PictureActivity; 19 | import com.jin.gank.R; 20 | import com.jin.gank.data.Constant; 21 | import com.jin.gank.data.GankCategory; 22 | import com.jin.gank.databinding.ItemImgBinding; 23 | import com.jin.gank.network.RetrofitHelp; 24 | import com.malinskiy.superrecyclerview.OnMoreListener; 25 | import com.malinskiy.superrecyclerview.SuperRecyclerView; 26 | import com.vlonjatg.progressactivity.ProgressActivity; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | import butterknife.Bind; 32 | import butterknife.ButterKnife; 33 | import jp.wasabeef.recyclerview.animators.OvershootInLeftAnimator; 34 | import rx.Observable; 35 | import rx.android.schedulers.AndroidSchedulers; 36 | import rx.schedulers.Schedulers; 37 | 38 | /** 39 | * Created by Administrator on 2015/8/25. 40 | */ 41 | public class MyImgFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener, OnMoreListener { 42 | private static final String TAG = "MyImgFragment"; 43 | @Bind(R.id.progressActivity) 44 | ProgressActivity mProgressActivity; 45 | @Bind(R.id.superRecyclerView_img) 46 | SuperRecyclerView mSuperRecyclerViewImg; 47 | 48 | 49 | private List mGirls; 50 | private String[] mCategoryArray; 51 | private LayoutInflater mInflater; 52 | private MyAdapter mAdapter; 53 | private boolean isLoadMore = true; 54 | 55 | @Override 56 | public void onCreate(@Nullable Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | mCategoryArray = getResources().getStringArray(R.array.category_list); 59 | mInflater = LayoutInflater.from(getActivity()); 60 | 61 | } 62 | 63 | @Nullable 64 | @Override 65 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 66 | container = (ViewGroup) inflater.inflate(R.layout.fragment_img, null, false); 67 | ButterKnife.bind(this, container); 68 | return container; 69 | } 70 | 71 | @Override 72 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 73 | super.onViewCreated(view, savedInstanceState); 74 | mSuperRecyclerViewImg.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); 75 | mSuperRecyclerViewImg.getRecyclerView().setItemAnimator(new OvershootInLeftAnimator()); 76 | mSuperRecyclerViewImg.setRefreshListener(this); 77 | mSuperRecyclerViewImg.setupMoreListener(this, 10); 78 | mSuperRecyclerViewImg.setRefreshingColorResources(android.R.color.holo_orange_light, android.R.color.holo_blue_light, android.R.color.holo_green_light, android.R.color.holo_red_light); 79 | if (mGirls == null || mGirls.size() == 0) { 80 | loadGirlData(mCategoryArray[0], Constant.API_COUNT, 1, true); 81 | } else { 82 | mAdapter.notifyDataSetChanged(); 83 | } 84 | } 85 | 86 | 87 | //加载数据 88 | private void loadGirlData(String category, int count, int page, boolean firstLoad) { 89 | if (firstLoad) { 90 | mProgressActivity.showLoading(); 91 | if (mGirls != null) 92 | mGirls.clear(); 93 | } 94 | Observable.zip(RetrofitHelp.getApi().listGankCategory(category, count, page) 95 | , RetrofitHelp.getApi().listGankCategory(mCategoryArray[5], count, page), (Girls, Videos) -> 96 | createGirlDataWithgetFreeVideoDesc(Girls, Videos) 97 | ).subscribeOn(Schedulers.io()) 98 | .observeOn(AndroidSchedulers.mainThread()) 99 | .flatMap(Girls -> { 100 | //判断是否已经加载到最后一页 101 | if (Girls.getResults().size() < 10) 102 | isLoadMore = false; 103 | 104 | return Observable.just(Girls.getResults()); 105 | }) 106 | .subscribe(Girls -> { 107 | mProgressActivity.showContent(); 108 | 109 | if (mAdapter == null) { 110 | mGirls = Girls; 111 | mAdapter = new MyAdapter(mGirls); 112 | mSuperRecyclerViewImg.setAdapter(mAdapter); 113 | } else { 114 | if (!firstLoad) 115 | mGirls.addAll(mGirls.size(), Girls); 116 | 117 | mAdapter.setGirls(mGirls); 118 | mAdapter.notifyDataSetChanged(); 119 | } 120 | 121 | mSuperRecyclerViewImg.hideMoreProgress(); 122 | mSuperRecyclerViewImg.getSwipeToRefresh().setRefreshing(false); 123 | }, 124 | err -> mProgressActivity.showError(null, "错误", err.toString(), "重试", v -> loadGirlData(category, Constant.API_COUNT, 1, true))); 125 | } 126 | 127 | private GankCategory createGirlDataWithgetFreeVideoDesc(GankCategory girls, GankCategory loves) { 128 | for (int i = 0; i < girls.getResults().size(); i++) { 129 | GankCategory.ResultsEntity girl = girls.getResults().get(i); 130 | if (loves.getResults() != null && loves.getResults().size() > i) 131 | girl.setDesc(girl.getDesc() + " " + loves.getResults().get(i).getDesc()); 132 | } 133 | return girls; 134 | } 135 | 136 | 137 | @Override 138 | public void onMoreAsked(int numberOfItems, int numberBeforeMore, int currentItemPos) { 139 | if (isLoadMore) { 140 | loadGirlData(mCategoryArray[0], Constant.API_COUNT, numberOfItems / Constant.API_COUNT + 1, false); 141 | } else { 142 | mSuperRecyclerViewImg.hideMoreProgress(); 143 | } 144 | } 145 | 146 | @Override 147 | public void onRefresh() { 148 | isLoadMore = true; 149 | loadGirlData(mCategoryArray[0], Constant.API_COUNT, 1, true); 150 | } 151 | 152 | 153 | private class MyAdapter extends RecyclerView.Adapter implements View.OnClickListener { 154 | 155 | public void setGirls(List girls) { 156 | this.girls = girls; 157 | } 158 | 159 | private List girls; 160 | 161 | public MyAdapter(List girls) { 162 | super(); 163 | this.girls = girls; 164 | } 165 | 166 | @Override 167 | public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 168 | View contentView = mInflater.inflate(R.layout.item_img, parent, false); 169 | MyViewHolder holder = new MyViewHolder(contentView); 170 | return holder; 171 | } 172 | 173 | @Override 174 | public void onBindViewHolder(MyViewHolder holder, final int position) { 175 | GankCategory.ResultsEntity gril = girls.get(position); 176 | holder.bind(gril); 177 | holder.mImgBinding.welfareImg.setOriginalSize(50, 50); 178 | holder.mImgBinding.welfareImg.setTag(R.id.image_tag, gril); 179 | holder.mImgBinding.welfareImg.setOnClickListener(this); 180 | holder.mImgBinding.welfareDesc.setTag(R.id.image_tag, gril); 181 | holder.mImgBinding.welfareDesc.setOnClickListener(this); 182 | 183 | } 184 | 185 | @Override 186 | public int getItemCount() { 187 | return girls.size(); 188 | } 189 | 190 | @Override 191 | public void onClick(View v) { 192 | startPictureActivity((GankCategory.ResultsEntity) v.getTag(R.id.image_tag), v); 193 | } 194 | 195 | 196 | class MyViewHolder extends RecyclerView.ViewHolder { 197 | private ItemImgBinding mImgBinding; 198 | 199 | public MyViewHolder(View convertView) { 200 | super(convertView); 201 | mImgBinding = DataBindingUtil.bind(convertView); 202 | 203 | } 204 | 205 | public void bind(GankCategory.ResultsEntity Gril) { 206 | mImgBinding.setGirl(Gril); 207 | } 208 | } 209 | 210 | private void startPictureActivity(GankCategory.ResultsEntity girl, View sharedView) { 211 | if (sharedView.getId() == R.id.welfare_img) { 212 | Intent intent = new Intent(getActivity(), PictureActivity.class); 213 | intent.putExtra(PictureActivity.EXTRA_IMAGE_URL, girl.getUrl()); 214 | intent.putExtra(PictureActivity.EXTRA_IMAGE_TITLE, girl.getDesc()); 215 | //打开activity 动画 216 | ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), sharedView, 217 | PictureActivity.TRANSIT_PIC); 218 | ActivityCompat.startActivity(getActivity(), intent, optionsCompat.toBundle()); 219 | } else { 220 | Intent intent = new Intent(getActivity(), GankActivity.class); 221 | int position = mGirls.indexOf(girl); 222 | List dates = new ArrayList<>(); 223 | //判断是否超出最大list大小 224 | if (position + 5 > mGirls.size()) { 225 | for (int i = position; i < mGirls.size(); i++) { 226 | String PublishedAt = mGirls.get(i).getPublishedAt().split("T")[0]; 227 | dates.add(PublishedAt); 228 | } 229 | } else { 230 | for (int i = position; i < position + 5; i++) { 231 | String PublishedAt = mGirls.get(i).getPublishedAt().split("T")[0]; 232 | dates.add(PublishedAt); 233 | } 234 | } 235 | // String PublishedAt = girl.getPublishedAt().split("T")[0]; 236 | intent.putStringArrayListExtra(GankActivity.EXTRA_GANK_DATA, (ArrayList) dates); 237 | startActivity(intent); 238 | } 239 | 240 | } 241 | 242 | } 243 | 244 | 245 | @Override 246 | public void onDestroyView() { 247 | super.onDestroyView(); 248 | ButterKnife.unbind(this); 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/network/RESTApi.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.network; 2 | 3 | import com.jin.gank.data.GankCategory; 4 | import com.jin.gank.data.GankDay; 5 | import com.jin.gank.data.GankRandom; 6 | 7 | import retrofit2.http.GET; 8 | import retrofit2.http.Path; 9 | import rx.Observable; 10 | 11 | /** 12 | * Created by Administrator on 2015/8/21. 13 | * 干货集中营 API 14 | */ 15 | public interface RESTApi { 16 | @GET("data/{category}/{count}/{page}") 17 | Observable listGankCategory(@Path("category") String category, @Path("count") int count, @Path("page") int page); 18 | 19 | 20 | @GET("day/{year}/{month}/{day}") 21 | Observable listGankDay(@Path("year") int year, @Path("month") int month, @Path("day") int day); 22 | 23 | @GET("random/data/{category}/{count}") 24 | Observable listGankRandom(@Path("category") String category, @Path("count") int count); 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/network/RetrofitHelp.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.network; 2 | 3 | import com.jin.gank.data.Constant; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import okhttp3.OkHttpClient; 8 | import retrofit2.Retrofit; 9 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 10 | import retrofit2.converter.gson.GsonConverterFactory; 11 | 12 | 13 | /** 14 | * Created by Administrator on 2015/8/21. 15 | */ 16 | public class RetrofitHelp { 17 | private static RESTApi api; 18 | 19 | public static RESTApi getApi() { 20 | if (api == null) { 21 | OkHttpClient client = new OkHttpClient.Builder() 22 | .connectTimeout(10, TimeUnit.SECONDS) 23 | .readTimeout(10, TimeUnit.SECONDS) 24 | .writeTimeout(10, TimeUnit.SECONDS) 25 | .retryOnConnectionFailure(true) 26 | .build(); 27 | 28 | Retrofit retrofit = new Retrofit.Builder().client(client) 29 | .baseUrl(Constant.ROOT_PATH) 30 | .addConverterFactory(GsonConverterFactory.create()) 31 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 32 | .build(); 33 | api = retrofit.create(RESTApi.class); 34 | } 35 | 36 | return api; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/util/DateUtils.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.util; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | 8 | 9 | public class DateUtils { 10 | 11 | public static String toDate(Date date) { 12 | DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); 13 | return dateFormat.format(date); 14 | } 15 | 16 | 17 | 18 | public static String toDate(Date date, int add) { 19 | Calendar calendar = Calendar.getInstance(); 20 | calendar.setTime(date); 21 | calendar.add(Calendar.DATE, add); 22 | return toDate(calendar.getTime()); 23 | } 24 | 25 | public static Date getLastdayDate(Date date) { 26 | Calendar calendar = Calendar.getInstance(); 27 | calendar.setTime(date); 28 | calendar.add(Calendar.DATE, -1); 29 | return calendar.getTime(); 30 | } 31 | 32 | public static Date getNextdayDate(Date date) { 33 | Calendar calendar = Calendar.getInstance(); 34 | calendar.setTime(date); 35 | calendar.add(Calendar.DATE, 1); 36 | return calendar.getTime(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/util/ImageUtils.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.util; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.net.Uri; 7 | import android.os.AsyncTask; 8 | import android.os.Environment; 9 | import android.provider.MediaStore; 10 | import com.bumptech.glide.Glide; 11 | import com.jin.gank.R; 12 | import java.io.File; 13 | import java.io.FileNotFoundException; 14 | import java.io.FileOutputStream; 15 | import java.io.IOException; 16 | import java.util.concurrent.ExecutionException; 17 | 18 | 19 | 20 | public class ImageUtils { 21 | 22 | public static void saveImageToSdCard(Context context, String url, String title) { 23 | TaskUtils.executeAsyncTask(new AsyncTask() { 24 | @Override 25 | protected Boolean doInBackground(Object... params) { 26 | Bitmap bmp = null; 27 | try { 28 | bmp = Glide.with(context).load(url).asBitmap().into(-1, -1).get(); 29 | } catch (InterruptedException e) { 30 | e.printStackTrace(); 31 | } catch (ExecutionException e) { 32 | e.printStackTrace(); 33 | } 34 | if (bmp == null) { 35 | return false; 36 | } 37 | 38 | // 首先保存图片 39 | File appDir = new File(Environment.getExternalStorageDirectory(), "Girl"); 40 | if (!appDir.exists()) { 41 | appDir.mkdir(); 42 | } 43 | String fileName = title.replace('/', '-') + ".jpg"; 44 | File file = new File(appDir, fileName); 45 | try { 46 | FileOutputStream fos = new FileOutputStream(file); 47 | bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos); 48 | fos.flush(); 49 | fos.close(); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | return false; 53 | } 54 | 55 | // 其次把文件插入到系统图库 56 | try { 57 | MediaStore.Images.Media.insertImage(context.getContentResolver(), 58 | file.getAbsolutePath(), fileName, null); 59 | } catch (FileNotFoundException e) { 60 | e.printStackTrace(); 61 | } 62 | 63 | // 最后通知图库更新 64 | Intent scannerIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, 65 | Uri.parse("file://" + file.getAbsolutePath())); 66 | context.sendBroadcast(scannerIntent); 67 | 68 | return true; 69 | } 70 | 71 | @Override 72 | protected void onPostExecute(Boolean result) { 73 | super.onPostExecute(result); 74 | String msg; 75 | if (result) { 76 | File appDir = new File(Environment.getExternalStorageDirectory(), "Girl"); 77 | if (!appDir.exists()) { 78 | appDir.mkdir(); 79 | } 80 | msg = String.format(context.getString(R.string.picture_has_save_to), 81 | appDir.getAbsolutePath()); 82 | } else { 83 | msg = context.getString(R.string.picture_save_fail); 84 | } 85 | ToastUtils.showShort(msg); 86 | } 87 | }); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/util/LoveStringUtils.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.util; 2 | 3 | 4 | public class LoveStringUtils { 5 | 6 | public static String getVideoPreviewImageUrl(String resp) { 7 | int s0 = resp.indexOf("

休息视频

"); 8 | if (s0 == -1) return null; 9 | int s1 = resp.indexOf(" void executeAsyncTask( 10 | AsyncTask task, Params... params) { 11 | if (Build.VERSION.SDK_INT >= 11) { 12 | task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 13 | } else { 14 | task.execute(params); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/util/ToastUtils.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.util; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import com.jin.gank.application.MyApplication; 7 | 8 | 9 | public class ToastUtils { 10 | 11 | Context mContext; 12 | 13 | private ToastUtils() { 14 | } 15 | 16 | private static void show(Context context, int resId, int duration) { 17 | Toast.makeText(context, resId, duration).show(); 18 | } 19 | 20 | private static void show(Context context, String message, int duration) { 21 | Toast.makeText(context, message, duration).show(); 22 | } 23 | 24 | public static void showShort(int resId) { 25 | Toast.makeText(MyApplication.sContext, resId, Toast.LENGTH_SHORT).show(); 26 | } 27 | 28 | public static void showShort(String message) { 29 | Toast.makeText(MyApplication.sContext, message, Toast.LENGTH_SHORT).show(); 30 | } 31 | 32 | public static void showLong(int resId) { 33 | Toast.makeText(MyApplication.sContext, resId, Toast.LENGTH_LONG).show(); 34 | } 35 | 36 | public static void showLong(String message) { 37 | Toast.makeText(MyApplication.sContext, message, Toast.LENGTH_LONG).show(); 38 | } 39 | 40 | public static void showLongLong(String message) { 41 | showLong(message); 42 | showLong(message); 43 | } 44 | 45 | public static void showLongLong(int resId) { 46 | showLong(resId); 47 | showLong(resId); 48 | } 49 | 50 | public static void showLongLongLong(int resId) { 51 | showLong(resId); 52 | showLong(resId); 53 | showShort(resId); 54 | } 55 | 56 | public static void showLongLongLong(String message) { 57 | showLong(message); 58 | showLong(message); 59 | showShort(message); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/view/GoodAppBarLayout.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.view; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.AppBarLayout; 5 | import android.util.AttributeSet; 6 | 7 | 8 | public class GoodAppBarLayout extends AppBarLayout { 9 | 10 | public int offset; 11 | OnOffsetChangedListener mOnOffsetChangedListener; 12 | 13 | public GoodAppBarLayout(Context context) { 14 | this(context, null); 15 | } 16 | 17 | public GoodAppBarLayout(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | public void notifyRemoveOffsetListener() { 22 | this.removeOnOffsetChangedListener(mOnOffsetChangedListener); 23 | } 24 | 25 | public void notifyAddOffsetListener() { 26 | this.addOnOffsetChangedListener(mOnOffsetChangedListener); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/view/LoveVideoView.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.webkit.WebSettings; 6 | import android.webkit.WebView; 7 | import android.webkit.WebViewClient; 8 | 9 | 10 | public class LoveVideoView extends WebView { 11 | 12 | public LoveVideoView(Context context) { 13 | this(context, null); 14 | } 15 | 16 | public LoveVideoView(Context context, AttributeSet attrs) { 17 | this(context, attrs, 0); 18 | } 19 | 20 | public LoveVideoView(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | init(); 23 | } 24 | 25 | void init() { 26 | setWebViewClient(new LoveClient()); 27 | WebSettings webSettings = getSettings(); 28 | webSettings.setJavaScriptEnabled(true); 29 | webSettings.setAllowFileAccess(true); 30 | webSettings.setDatabaseEnabled(true); 31 | webSettings.setDomStorageEnabled(true); 32 | webSettings.setSaveFormData(false); 33 | webSettings.setAppCacheEnabled(true); 34 | webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); 35 | webSettings.setLoadWithOverviewMode(false); 36 | webSettings.setUseWideViewPort(true); 37 | } 38 | 39 | private class LoveClient extends WebViewClient { 40 | 41 | @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { 42 | view.loadUrl(url); 43 | return true; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/view/PLAImageView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 [1076559197@qq.com | tchen0707@gmail.com] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License”); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.jin.gank.view; 18 | 19 | import android.content.Context; 20 | import android.graphics.Bitmap; 21 | import android.graphics.drawable.Drawable; 22 | import android.util.AttributeSet; 23 | import android.widget.ImageView; 24 | import android.widget.RelativeLayout; 25 | 26 | 27 | public class PLAImageView extends ImageView { 28 | private Bitmap currentBitmap; 29 | private ImageChangeListener imageChangeListener; 30 | private boolean scaleToWidth = false; // this flag determines if should 31 | // measure height manually dependent 32 | // of width 33 | 34 | public PLAImageView(Context context) { 35 | super(context); 36 | init(); 37 | } 38 | 39 | public PLAImageView(Context context, AttributeSet attrs, int defStyle) { 40 | super(context, attrs, defStyle); 41 | init(); 42 | } 43 | 44 | public PLAImageView(Context context, AttributeSet attrs) { 45 | super(context, attrs); 46 | init(); 47 | } 48 | 49 | private void init() { 50 | this.setScaleType(ScaleType.CENTER_INSIDE); 51 | } 52 | 53 | public void recycle() { 54 | setImageBitmap(null); 55 | if ((this.currentBitmap == null) || (this.currentBitmap.isRecycled())) 56 | return; 57 | this.currentBitmap.recycle(); 58 | this.currentBitmap = null; 59 | } 60 | 61 | @Override 62 | public void setImageBitmap(Bitmap bm) { 63 | currentBitmap = bm; 64 | super.setImageBitmap(currentBitmap); 65 | if (imageChangeListener != null) 66 | imageChangeListener.changed((currentBitmap == null)); 67 | } 68 | 69 | @Override 70 | public void setImageDrawable(Drawable d) { 71 | super.setImageDrawable(d); 72 | if (imageChangeListener != null) 73 | imageChangeListener.changed((d == null)); 74 | } 75 | 76 | @Override 77 | public void setImageResource(int id) { 78 | super.setImageResource(id); 79 | } 80 | 81 | public interface ImageChangeListener { 82 | // a callback for when a change has been made to this imageView 83 | void changed(boolean isEmpty); 84 | } 85 | 86 | public ImageChangeListener getImageChangeListener() { 87 | return imageChangeListener; 88 | } 89 | 90 | public void setImageChangeListener(ImageChangeListener imageChangeListener) { 91 | this.imageChangeListener = imageChangeListener; 92 | } 93 | 94 | private int imageWidth; 95 | private int imageHeight; 96 | 97 | public void setImageWidth(int w) { 98 | imageWidth = w; 99 | } 100 | 101 | public void setImageHeight(int h) { 102 | imageHeight = h; 103 | } 104 | 105 | @Override 106 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 107 | 108 | int widthMode = MeasureSpec.getMode(widthMeasureSpec); 109 | int heightMode = MeasureSpec.getMode(heightMeasureSpec); 110 | int width = MeasureSpec.getSize(widthMeasureSpec); 111 | int height = MeasureSpec.getSize(heightMeasureSpec); 112 | 113 | /** 114 | * if both width and height are set scale width first. modify in future 115 | * if necessary 116 | */ 117 | 118 | if (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST) { 119 | scaleToWidth = true; 120 | } else if (heightMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.AT_MOST) { 121 | scaleToWidth = false; 122 | } else 123 | throw new IllegalStateException("width or height needs to be set to match_parent or a specific dimension"); 124 | 125 | if (imageWidth == 0) { 126 | // nothing to measure 127 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 128 | return; 129 | } else { 130 | if (scaleToWidth) { 131 | int iw = imageWidth; 132 | int ih = imageHeight; 133 | int heightC = width * ih / iw; 134 | if (height > 0) 135 | if (heightC > height) { 136 | // dont let hegiht be greater then set max 137 | heightC = height; 138 | width = heightC * iw / ih; 139 | } 140 | 141 | this.setScaleType(ScaleType.CENTER_CROP); 142 | setMeasuredDimension(width, heightC); 143 | 144 | } else { 145 | // need to scale to height instead 146 | int marg = 0; 147 | if (getParent() != null) { 148 | if (getParent().getParent() != null) { 149 | marg += ((RelativeLayout) getParent().getParent()).getPaddingTop(); 150 | marg += ((RelativeLayout) getParent().getParent()).getPaddingBottom(); 151 | } 152 | } 153 | 154 | int iw = imageWidth; 155 | int ih = imageHeight; 156 | 157 | width = height * iw / ih; 158 | height -= marg; 159 | setMeasuredDimension(width, height); 160 | } 161 | 162 | } 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/view/RatioImageView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 XiNGRZ 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.jin.gank.view; 18 | 19 | import android.content.Context; 20 | import android.util.AttributeSet; 21 | import android.widget.ImageView; 22 | 23 | /** 24 | * 一个能保持比例的 ImageView 25 | * TODO: 暂时只支持维持宽度适应高度 26 | */ 27 | public class RatioImageView extends ImageView { 28 | 29 | private int originalWidth = 50; 30 | private int originalHeight = 50; 31 | 32 | public RatioImageView(Context context) { 33 | super(context); 34 | } 35 | 36 | public RatioImageView(Context context, AttributeSet attrs) { 37 | super(context, attrs); 38 | } 39 | 40 | public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | } 43 | 44 | public void setOriginalSize(int originalWidth, int originalHeight) { 45 | this.originalWidth = originalWidth; 46 | this.originalHeight = originalHeight; 47 | } 48 | 49 | 50 | @Override 51 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 52 | if (originalWidth > 0 && originalHeight > 0) { 53 | float ratio = (float) originalWidth / (float) originalHeight; 54 | 55 | int width = MeasureSpec.getSize(widthMeasureSpec); 56 | int height = MeasureSpec.getSize(heightMeasureSpec); 57 | 58 | // TODO: 现在只支持固定宽度 59 | if (width > 0) { 60 | height = (int) ((float) width / ratio); 61 | } 62 | 63 | setMeasuredDimension(width, height); 64 | } else { 65 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/jin/gank/view/VideoImageView.java: -------------------------------------------------------------------------------- 1 | package com.jin.gank.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ImageView; 6 | 7 | import com.nineoldandroids.animation.Animator; 8 | import com.nineoldandroids.animation.AnimatorSet; 9 | import com.nineoldandroids.animation.ObjectAnimator; 10 | 11 | 12 | public class VideoImageView extends ImageView implements Animator.AnimatorListener { 13 | 14 | private boolean scale = false; 15 | 16 | public VideoImageView(Context context) { 17 | this(context, null); 18 | } 19 | 20 | public VideoImageView(Context context, AttributeSet attrs) { 21 | this(context, attrs, 0); 22 | } 23 | 24 | public VideoImageView(Context context, AttributeSet attrs, int defStyleAttr) { 25 | super(context, attrs, defStyleAttr); 26 | nextAnimation(); 27 | } 28 | 29 | private void nextAnimation() { 30 | // TODO: remove 9 old android. 31 | AnimatorSet anim = new AnimatorSet(); 32 | if (scale) { 33 | anim.playTogether(ObjectAnimator.ofFloat(this, "scaleX", 1.5f, 1f), 34 | ObjectAnimator.ofFloat(this, "scaleY", 1.5f, 1f)); 35 | } else { 36 | anim.playTogether(ObjectAnimator.ofFloat(this, "scaleX", 1, 1.5f), 37 | ObjectAnimator.ofFloat(this, "scaleY", 1, 1.5f)); 38 | } 39 | 40 | anim.setDuration(10987); 41 | anim.addListener(this); 42 | anim.start(); 43 | scale = !scale; 44 | } 45 | 46 | @Override public void onAnimationCancel(Animator arg0) { 47 | } 48 | 49 | @Override public void onAnimationEnd(Animator animator) { 50 | nextAnimation(); 51 | } 52 | 53 | @Override public void onAnimationRepeat(Animator arg0) { 54 | } 55 | 56 | @Override public void onAnimationStart(Animator arg0) { 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gride_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/line_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_gank.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_picture.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_web.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 18 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_main_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 26 | 27 | 37 | 38 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 26 | 27 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_toolbar_with_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 18 | 19 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_toolbar_with_title_view.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_gank.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 20 | 21 | 28 | 29 | 33 | 34 | 40 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 60 | 61 | 67 | 68 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_img.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 11 | 12 | 21 | 22 | 28 | 29 | 37 | 38 | 48 | 58 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_gank.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 31 | 32 | 37 | 38 | 41 | 42 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_img.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 11 | 12 | 21 | 22 | 27 | 28 | 36 | 37 | 49 | 50 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_more_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_gank.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 17 | 18 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_picture.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/app/src/main/res/mipmap-xhdpi/dropdown.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/app/src/main/res/mipmap-xhdpi/ic_play.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/app/src/main/res/mipmap-xhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/android_material_design_colours.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | #fde0dc 9 | #f9bdbb 10 | #f69988 11 | #f36c60 12 | #e84e40 13 | #e51c23 14 | #dd191d 15 | #d01716 16 | #c41411 17 | #b0120a 18 | #ff7997 19 | #ff5177 20 | #ff2d6f 21 | #e00032 22 | 23 | 24 | #fce4ec 25 | #f8bbd0 26 | #f48fb1 27 | #f06292 28 | #ec407a 29 | #e91e63 30 | #d81b60 31 | #c2185b 32 | #ad1457 33 | #880e4f 34 | #ff80ab 35 | #ff4081 36 | #f50057 37 | #c51162 38 | 39 | 40 | #f3e5f5 41 | #e1bee7 42 | #ce93d8 43 | #ba68c8 44 | #ab47bc 45 | #9c27b0 46 | #8e24aa 47 | #7b1fa2 48 | #6a1b9a 49 | #4a148c 50 | #ea80fc 51 | #e040fb 52 | #d500f9 53 | #aa00ff 54 | 55 | 56 | #ede7f6 57 | #d1c4e9 58 | #b39ddb 59 | #9575cd 60 | #7e57c2 61 | #673ab7 62 | #5e35b1 63 | #512da8 64 | #4527a0 65 | #311b92 66 | #b388ff 67 | #7c4dff 68 | #651fff 69 | #6200ea 70 | 71 | 72 | #e8eaf6 73 | #c5cae9 74 | #9fa8da 75 | #7986cb 76 | #5c6bc0 77 | #3f51b5 78 | #3949ab 79 | #303f9f 80 | #283593 81 | #1a237e 82 | #8c9eff 83 | #536dfe 84 | #3d5afe 85 | #304ffe 86 | 87 | 88 | #e7e9fd 89 | #d0d9ff 90 | #afbfff 91 | #91a7ff 92 | #738ffe 93 | #5677fc 94 | #4e6cef 95 | #455ede 96 | #3b50ce 97 | #2a36b1 98 | #a6baff 99 | #6889ff 100 | #4d73ff 101 | #4d69ff 102 | 103 | 104 | #e1f5fe 105 | #b3e5fc 106 | #81d4fa 107 | #4fc3f7 108 | #29b6f6 109 | #03a9f4 110 | #039be5 111 | #0288d1 112 | #0277bd 113 | #01579b 114 | #80d8ff 115 | #40c4ff 116 | #00b0ff 117 | #0091ea 118 | 119 | 120 | #e0f7fa 121 | #b2ebf2 122 | #80deea 123 | #4dd0e1 124 | #26c6da 125 | #00bcd4 126 | #00acc1 127 | #0097a7 128 | #00838f 129 | #006064 130 | #84ffff 131 | #18ffff 132 | #00e5ff 133 | #00b8d4 134 | 135 | 136 | #e0f2f1 137 | #b2dfdb 138 | #80cbc4 139 | #4db6ac 140 | #26a69a 141 | #009688 142 | #00897b 143 | #00796b 144 | #00695c 145 | #004d40 146 | #a7ffeb 147 | #64ffda 148 | #1de9b6 149 | #00bfa5 150 | 151 | 152 | #d0f8ce 153 | #a3e9a4 154 | #72d572 155 | #42bd41 156 | #2baf2b 157 | #259b24 158 | #0a8f08 159 | #0a7e07 160 | #056f00 161 | #0d5302 162 | #a2f78d 163 | #5af158 164 | #14e715 165 | #12c700 166 | 167 | 168 | #f1f8e9 169 | #dcedc8 170 | #c5e1a5 171 | #aed581 172 | #9ccc65 173 | #8bc34a 174 | #7cb342 175 | #689f38 176 | #558b2f 177 | #33691e 178 | #ccff90 179 | #b2ff59 180 | #76ff03 181 | #64dd17 182 | 183 | 184 | #f9fbe7 185 | #f0f4c3 186 | #e6ee9c 187 | #dce775 188 | #d4e157 189 | #cddc39 190 | #c0ca33 191 | #afb42b 192 | #9e9d24 193 | #827717 194 | #f4ff81 195 | #eeff41 196 | #c6ff00 197 | #aeea00 198 | 199 | 200 | #fffde7 201 | #fff9c4 202 | #fff59d 203 | #fff176 204 | #ffee58 205 | #ffeb3b 206 | #fdd835 207 | #fbc02d 208 | #f9a825 209 | #f57f17 210 | #ffff8d 211 | #ffff00 212 | #ffea00 213 | #ffd600 214 | 215 | 216 | #fff8e1 217 | #ffecb3 218 | #ffe082 219 | #ffd54f 220 | #ffca28 221 | #ffc107 222 | #ffb300 223 | #ffa000 224 | #ff8f00 225 | #ff6f00 226 | #ffe57f 227 | #ffd740 228 | #ffc400 229 | #ffab00 230 | 231 | 232 | #fff3e0 233 | #ffe0b2 234 | #ffcc80 235 | #ffb74d 236 | #ffa726 237 | #ff9800 238 | #fb8c00 239 | #f57c00 240 | #ef6c00 241 | #e65100 242 | #ffd180 243 | #ffab40 244 | #ff9100 245 | #ff6d00 246 | 247 | 248 | #fbe9e7 249 | #ffccbc 250 | #ffab91 251 | #ff8a65 252 | #ff7043 253 | #ff5722 254 | #f4511e 255 | #e64a19 256 | #d84315 257 | #bf360c 258 | #ff9e80 259 | #ff6e40 260 | #ff3d00 261 | #dd2c00 262 | 263 | 264 | #efebe9 265 | #d7ccc8 266 | #bcaaa4 267 | #a1887f 268 | #8d6e63 269 | #795548 270 | #6d4c41 271 | #5d4037 272 | #4e342e 273 | #3e2723 274 | 275 | 276 | #fafafa 277 | #f5f5f5 278 | #eeeeee 279 | #e0e0e0 280 | #bdbdbd 281 | #9e9e9e 282 | #757575 283 | #616161 284 | #424242 285 | #212121 286 | #000000 287 | #ffffff 288 | 289 | 290 | #eceff1 291 | #cfd8dc 292 | #b0bec5 293 | #90a4ae 294 | #78909c 295 | #607d8b 296 | #546e7a 297 | #455a64 298 | #37474f 299 | #263238 300 | 301 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 福利 23 | Android 24 | iOS 25 | all 26 | 前端 27 | 休息视频 28 | 拓展资源 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FF5722 5 | #E64A19 6 | #FF5722 7 | 8 | 9 | 10 | #ffffff 11 | #000000 12 | #00ffffff 13 | #03a9f4 14 | #009688 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 10dp 7 | 12sp 8 | 14sp 9 | 224dp 10 | 16dp 11 | 18sp 12 | 8dp 13 | 38dp 14 | 13sp 15 | 14sp 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 干货集锦 3 | 作        者: 4 | 发布日期: 5 | 功能描述: 6 | 链        接: 7 | 8 | 关于 9 | 分享 10 | 保存图片 11 | 图片已保存至 %s 文件夹 12 | 图片保存失败,请再次尝试保存! 13 | 发现了一款非常美观的App「干货集锦」,每天一张精选妹纸图、一个精选小视频(视频源地址播放),一篇程序猿干货,完全开源不收费,太赞了! 14 | 参考了http://fir.im/mengmeizhi的项目 推荐~:https://github.com/jtsky/gank.git 15 | 16 | http://gank.io/ 17 | 加载中...(这其实不是一个直接的视频播放器,而是个网页浏览器,需要你手动点击页面中的视频进行播放,另外,特别照顾魅族用户,可以按 18 | 下音量键 来返回竖屏页面) 19 | 20 | 今日は肥皂家がない、干物の供給がない... 21 | 今日肥皂家话题 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 23 | 24 | 28 | 29 | 37 | 38 | 42 | 43 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.0-beta2' 9 | //classpath 'com.android.tools.build:gradle:1.3.0' 10 | classpath 'me.tatarka:gradle-retrolambda:3.2.5' 11 | //classpath 'org.jsonschema2pojo:jsonschema2pojo-gradle-plugin:latest.integration' 12 | //classpath "com.android.databinding:dataBinder:1.0-rc1" 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | jcenter() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 22 11:06:08 CST 2016 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.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /imgs/big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/imgs/big.png -------------------------------------------------------------------------------- /imgs/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/imgs/category.png -------------------------------------------------------------------------------- /imgs/girl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtsky/gank/921eb827f30edd6aa8bdfe6f603cf016b003ba20/imgs/girl.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------