├── .gitignore ├── README.md ├── adapter ├── .gitignore ├── build.gradle ├── nexus-push.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── fanneng │ │ └── android │ │ └── adapter │ │ └── provider │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── fanneng │ │ │ └── android │ │ │ └── adapter │ │ │ └── provider │ │ │ ├── IItemBean.java │ │ │ ├── IViewProvider.java │ │ │ ├── LifeCycleListener.java │ │ │ └── ProviderAdapter.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── fanneng │ └── android │ └── adapter │ └── provider │ └── ExampleUnitTest.java ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── fanneng │ │ └── android │ │ └── adapter │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── fanneng │ │ │ └── android │ │ │ └── adapter │ │ │ ├── Data.java │ │ │ ├── MainActivity.java │ │ │ ├── MainPageData.java │ │ │ ├── template │ │ │ ├── action │ │ │ │ ├── ActionCardView.java │ │ │ │ ├── ActionData.java │ │ │ │ └── ActionsProvider.java │ │ │ ├── banner │ │ │ │ ├── BannerData.java │ │ │ │ └── BannerProvider.java │ │ │ ├── guess │ │ │ │ ├── GuessData.java │ │ │ │ └── GuessProvider.java │ │ │ ├── headline │ │ │ │ ├── HeadLineData.java │ │ │ │ └── HeadLineProvider.java │ │ │ └── recommend │ │ │ │ ├── RecommendData.java │ │ │ │ └── RecommendProvider.java │ │ │ └── utils │ │ │ ├── GlideImageLoader.java │ │ │ ├── GlideRoundTransform.java │ │ │ └── UIUtils.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── layout_toolbar.xml │ │ ├── template_action_cardview.xml │ │ ├── template_guess.xml │ │ ├── template_headline.xml │ │ ├── template_headline_marquee_item.xml │ │ ├── template_recommend.xml │ │ └── template_slideshow.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── home_camera.png │ │ ├── home_msg.png │ │ ├── home_scan.png │ │ ├── home_search.png │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ ├── icon_headline.jpeg │ │ └── icon_recommend.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── vpi_defaults.xml │ └── test │ └── java │ └── com │ └── fanneng │ └── android │ └── adapter │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── provideradapter.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #built application files 2 | *.apk 3 | *.ap_ 4 | 5 | 6 | # files for the dex VM 7 | *.dex 8 | 9 | 10 | # Java class files 11 | *.class 12 | *.hprof 13 | 14 | # generated files 15 | bin/ 16 | gen/ 17 | 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Windows thumbnail db 23 | Thumbs.db 24 | 25 | 26 | # OSX files 27 | .DS_Store 28 | 29 | 30 | # Eclipse project files 31 | .classpath 32 | .project 33 | 34 | 35 | # Android Studio 36 | .idea 37 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs. 38 | .gradle 39 | build/ 40 | release/ 41 | flavorTest/ 42 | flavorRelease/ 43 | 44 | # Signing files 45 | .signing/ 46 | 47 | 48 | # User-specific configurations 49 | .idea/libraries/ 50 | .idea/workspace.xml 51 | .idea/tasks.xml 52 | .idea/.name 53 | .idea/compiler.xml 54 | .idea/copyright/profiles_settings.xml 55 | .idea/encodings.xml 56 | .idea/misc.xml 57 | .idea/modules.xml 58 | .idea/scopes/scope_settings.xml 59 | .idea/vcs.xml 60 | *.iml 61 | 62 | 63 | # fastlane 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ProviderAdapter 模板化开发 2 | 3 | #### 一、简介 4 | 5 | RecyclerView为何在大厂还不够普及?因为他们ListView封装的足够好! 6 | 7 | 本项目注重如何性能最优、如何敏捷开发、如何功能划分 8 | 9 | 所以,刚入手可能会觉得比常规写adapter会多出几个类,几行代码。以手淘项目为例。 10 | 11 | #### 二、初衷 12 | 13 | * 复杂界面adapter代码量过大,过多的类型判断难以阅读。 14 | 15 | * 每次迭代、维护都是一个头痛的问题,改其中一处就得动整个adapter,工作量过大,并且改动风险也大 16 | 17 | * 一个界面业务功能过多,业务牵连广泛,如何责任划分、协同开发? 18 | 19 | * 拆分View采用模板方式,可以最大化的View复用。 20 | 21 | #### 三、核心类 22 | 23 | * 源码都有注释 这里不做详细介绍 24 | 25 | [ProviderAdapter](adapter/src/main/java/com/fanneng/android/adapter/provider/ProviderAdapter.java) 26 | 27 | * 模板普通写法 28 | 29 | [GuessProvider](app/src/main/java/com/fanneng/android/adapter/template/recommend/RecommendProvider.java)、[BannerProvider](app/src/main/java/com/fanneng/android/adapter/template/banner/BannerProvider.java) 30 | 31 | * 模板进阶写法 32 | 33 | [action](app/src/main/java/com/fanneng/android/adapter/template/action) 34 | 35 | 此写法无任何xml,无任何inflate操作,并且添加了缓存机制,尽可能的减小了内存消耗 36 | 37 | #### 四、项目效果图 38 | 39 | ![点我查看效果图](provideradapter.gif) 40 | 41 | 42 | #### License 43 | 44 | ``` 45 | Copyright [2018] [Victory-Over] 46 | 47 | Licensed under the Apache License, Version 2.0 (the "License"); 48 | you may not use this file except in compliance with the License. 49 | You may obtain a copy of the License at 50 | 51 | http://www.apache.org/licenses/LICENSE-2.0 52 | 53 | Unless required by applicable law or agreed to in writing, software 54 | distributed under the License is distributed on an "AS IS" BASIS, 55 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 56 | See the License for the specific language governing permissions and 57 | limitations under the License. 58 | ``` 59 | -------------------------------------------------------------------------------- /adapter/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /adapter/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | 6 | 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 27 11 | versionCode 1 12 | versionName "1.0.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | 30 | implementation 'com.android.support:appcompat-v7:27.1.1' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 34 | } 35 | 36 | //打包到maven库 37 | apply from: './nexus-push.gradle' 38 | -------------------------------------------------------------------------------- /adapter/nexus-push.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | 3 | task androidJavadocs(type: Javadoc) { 4 | source = android.sourceSets.main.java.srcDirs 5 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 6 | } 7 | 8 | task androidSourcesJar(type: Jar) { 9 | classifier = 'sources' 10 | from android.sourceSets.main.java.srcDirs 11 | } 12 | 13 | artifacts { 14 | archives androidSourcesJar 15 | } 16 | 17 | //任务名 18 | uploadArchives { 19 | repositories { 20 | mavenDeployer { 21 | //这里的url是nexus中maven-releases的路径,可以点击copy按钮查看复制 22 | repository(url: "http://maven.enncloud.cn/content/repositories/releases/") { 23 | // nexus账号的用户名和密码,我这里没用默认的admin 24 | authentication(userName: "deployment", password: "deployment123") 25 | } 26 | 27 | // library的包名 28 | pom.groupId = 'com.fanneng.android' 29 | // library的项目名 30 | pom.artifactId = 'adapter' 31 | // library的版本号 32 | pom.version = "1.0.0" 33 | 34 | description 'fanneng appsupport aar' 35 | 36 | pom.project { 37 | licenses { 38 | license { 39 | name 'The Apache Software License, Version 2.0' 40 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /adapter/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /adapter/src/androidTest/java/com/fanneng/android/adapter/provider/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.provider; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.fanneng.android.adapter.provider.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /adapter/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /adapter/src/main/java/com/fanneng/android/adapter/provider/IItemBean.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.provider; 2 | 3 | /** 4 | * describe: 5 | * 6 | * @author :鲁宇峰 on 2018/9/14 9:22 7 | * email:luyufengc@enn.cn 8 | */ 9 | public interface IItemBean { 10 | public abstract Class getViewProviderClass(); 11 | } 12 | -------------------------------------------------------------------------------- /adapter/src/main/java/com/fanneng/android/adapter/provider/IViewProvider.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.provider; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | 7 | /** 8 | * describe: 9 | * 10 | * @author :鲁宇峰 on 2018/9/15 12:12 11 | * email:luyufengc@enn.cn 12 | */ 13 | public interface IViewProvider { 14 | public abstract View getItemView(Context context, View convertView, LayoutInflater inflater, Object data, int position); 15 | } 16 | -------------------------------------------------------------------------------- /adapter/src/main/java/com/fanneng/android/adapter/provider/LifeCycleListener.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.provider; 2 | 3 | /** 4 | * describe: 5 | * 6 | * @author :鲁宇峰 on 2018/9/15 10:17 7 | * email:luyufengc@enn.cn 8 | */ 9 | public interface LifeCycleListener { 10 | public void onDestroy(); 11 | public void onResume(); 12 | public void onPause(); 13 | } 14 | -------------------------------------------------------------------------------- /adapter/src/main/java/com/fanneng/android/adapter/provider/ProviderAdapter.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.provider; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * describe: 15 | * 16 | * @author :鲁宇峰 on 2018/9/14 11:17 17 | * email:luyufengc@enn.cn 18 | */ 19 | public class ProviderAdapter extends BaseAdapter { 20 | private Context mContext; 21 | private List itemBeanList; 22 | private List mProviders = new ArrayList(); 23 | private LayoutInflater mInflater; 24 | 25 | public ProviderAdapter(Context context, List> providers) { 26 | init(context, providers, null); 27 | } 28 | 29 | public ProviderAdapter(Context context, List> providers, List itemBeanList) { 30 | init(context, providers, itemBeanList); 31 | } 32 | 33 | private void init(Context context, List> providers, List itemBeanList) { 34 | this.mContext = context; 35 | mInflater = LayoutInflater.from(context); 36 | this.itemBeanList = itemBeanList; 37 | if (providers == null || providers.size() < 1) { 38 | throw new IllegalArgumentException("providers must not null or size < 1"); 39 | } 40 | //将所有模板添加进List 41 | for (Class cl : providers) { 42 | IViewProvider provider = null; 43 | Exception illegalException = null; 44 | try { 45 | provider = cl.newInstance(); 46 | }catch (Exception e){ 47 | illegalException = e; 48 | e.printStackTrace(); 49 | } 50 | 51 | if(provider == null){ 52 | if(illegalException != null){ 53 | illegalException.printStackTrace(); 54 | } 55 | throw new IllegalArgumentException(cl.getName() + "not add this provider"); 56 | 57 | } 58 | mProviders.add(provider); 59 | } 60 | } 61 | 62 | @Override 63 | public View getView(final int position, View convertView, ViewGroup parent) { 64 | IItemBean itemBean = itemBeanList.get(position); 65 | if (itemBean.getViewProviderClass() == null) { 66 | throw new IllegalArgumentException(itemBean + " getViewProviderClass() return not null"); 67 | } 68 | //得到bean 所有bean中实现了getViewProviderClass方法,从此方法可获得对应的模板provider 69 | IViewProvider viewProvider = getProvider(itemBean.getViewProviderClass()); 70 | if (viewProvider == null) { 71 | throw new IllegalArgumentException(itemBean.getViewProviderClass().getName() + " provider not exist"); 72 | } 73 | //得到模板之后 直接调用模板实现的getItemView方法 74 | convertView = viewProvider.getItemView(mContext, convertView, mInflater, itemBean, position); 75 | return convertView; 76 | } 77 | 78 | public List getItemBeanList() { 79 | return itemBeanList; 80 | } 81 | 82 | public void setItemBeanList(List itemBeanList) { 83 | if (itemBeanList == null) { 84 | return; 85 | } 86 | this.itemBeanList = itemBeanList; 87 | } 88 | 89 | @Override 90 | public long getItemId(int position) { 91 | return position; 92 | } 93 | 94 | @Override 95 | public IItemBean getItem(int position) { 96 | if (itemBeanList != null && position < itemBeanList.size() && position >= 0) { 97 | return itemBeanList.get(position); 98 | } 99 | return null; 100 | } 101 | 102 | @Override 103 | public int getCount() { 104 | return itemBeanList == null ? 0 : itemBeanList.size(); 105 | } 106 | 107 | public void clear() { 108 | if (itemBeanList != null && itemBeanList.size() > 0) { 109 | itemBeanList.clear(); 110 | } 111 | notifyDataSetChanged(); 112 | } 113 | 114 | @Override 115 | public int getItemViewType(int position) { 116 | 117 | //循环所有的模板,返回对应的ViewType 118 | if (itemBeanList == null || position < 0 || position >= itemBeanList.size()) { 119 | return 0; 120 | } 121 | IItemBean item = itemBeanList.get(position); 122 | if (item.getViewProviderClass() == null) { 123 | throw new IllegalArgumentException("ItemBean implements method getViewProvider() return not null"); 124 | } 125 | Class providerClass = item.getViewProviderClass(); 126 | int size = mProviders.size(); 127 | for (int i = 0; i < size; i++) { 128 | if (providerClass.isInstance(mProviders.get(i))) { 129 | return i; 130 | } 131 | } 132 | return 0; 133 | } 134 | 135 | @Override 136 | public int getViewTypeCount() { 137 | int typeSize = mProviders.size(); 138 | return typeSize <= 0 ? 1 : typeSize; 139 | } 140 | 141 | public IViewProvider getProvider(Class cls){ 142 | if(mProviders == null || mProviders.isEmpty()){ 143 | return null; 144 | } 145 | for(IViewProvider provider : mProviders){ 146 | if(cls.isInstance(provider)){ 147 | return provider; 148 | } 149 | } 150 | return null; 151 | } 152 | 153 | public void destroy(){ 154 | if(mProviders == null || mProviders.isEmpty()){ 155 | return; 156 | } 157 | for(IViewProvider provider : mProviders){ 158 | if(LifeCycleListener.class.isInstance(provider)){ 159 | ((LifeCycleListener)provider).onDestroy(); 160 | } 161 | } 162 | } 163 | 164 | public void onResume(){ 165 | if(mProviders == null || mProviders.isEmpty()){ 166 | return; 167 | } 168 | for(IViewProvider provider : mProviders){ 169 | if(LifeCycleListener.class.isInstance(provider)){ 170 | ((LifeCycleListener)provider).onResume(); 171 | } 172 | } 173 | } 174 | 175 | public void onPause(){ 176 | if(mProviders == null || mProviders.isEmpty()){ 177 | return; 178 | } 179 | for(IViewProvider provider : mProviders){ 180 | if(LifeCycleListener.class.isInstance(provider)){ 181 | ((LifeCycleListener)provider).onPause(); 182 | } 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /adapter/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | adapter 3 | 4 | -------------------------------------------------------------------------------- /adapter/src/test/java/com/fanneng/android/adapter/provider/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.provider; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.fanneng.android.adapter" 7 | minSdkVersion 14 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:27.1.1' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | 29 | compile 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-14' 30 | 31 | //轮播图 32 | compile 'com.youth.banner:banner:1.4.10' 33 | //滚动自定义控件 34 | compile 'com.oushangfeng:MarqueeLayout:1.0.8' 35 | //图片加载 36 | compile "com.github.bumptech.glide:glide:3.7.0" 37 | compile project(path: ':adapter') 38 | } 39 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/fanneng/android/adapter/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.fanneng.android.adapter", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/Data.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter; 2 | 3 | import com.fanneng.android.adapter.template.guess.GuessData; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * describe: 10 | * 11 | * @author :鲁宇峰 on 2018/9/19 19:29 12 | * email:luyufengc@enn.cn 13 | */ 14 | public class Data { 15 | public static List bannerList; 16 | 17 | 18 | public static String[] iconUrl = new String[]{ 19 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/d598a8b1bf2ffa589977d7e80f53f89c.jpg", 20 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/7d9109a8e3dee4227f1633863baaa7c4.jpg", 21 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/91d135fd2175b85caf67eb14bba5e05f.jpg", 22 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/da6c8ff52bbea0e83765a8a104310b87.jpg", 23 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/c456b623be37502712734f9d267a6236.jpg", 24 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/de83d2da6e518a609c07e5c0ceafd0c2.jpg", 25 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/dfb08eed327649412fbcd8e37fb06575.jpg", 26 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/d5743aa6115bf1be61bd168d10777251.jpg", 27 | "https://pic.iqshw.com/d/file/gexingqqziyuan/touxiang/2017/08/28/68260d930fb7ba190b9530c7709188b8.jpg" 28 | }; 29 | 30 | public static String[] titles = new String[]{"天猫", "聚划算", "天猫国际", "外卖", "天猫超市", "充值中心", "飞猪旅行", "领金币", "到家", "分类"}; 31 | 32 | public static List headLines; 33 | 34 | public static List guessList; 35 | 36 | static { 37 | bannerList = new ArrayList<>(); 38 | bannerList.add("https://img.alicdn.com/simba/img/TB1AhFnPVXXXXa.XFXXSutbFXXX.jpg_q50.jpg"); 39 | bannerList.add("https://gw.alicdn.com/imgextra/i2/118/TB22AjYg7qvpuFjSZFhXXaOgXXa_!!118-0-yamato.jpg_q50.jpg"); 40 | bannerList.add("https://gw.alicdn.com/imgextra/i1/34/TB2Kea5fYVkpuFjSspcXXbSMVXa_!!34-0-yamato.jpg_q50.jpg"); 41 | bannerList.add("https://gw.alicdn.com/imgextra/i1/101/TB2Dz1ScYtlpuFjSspoXXbcDpXa_!!101-0-yamato.jpg_q50.jpg"); 42 | bannerList.add("https://gw.alicdn.com/imgextra/i4/140/TB2lWFJgmFjpuFjSspbXXXagVXa_!!140-0-yamato.jpg_q50.jpg"); 43 | bannerList.add("https://aecpm.alicdn.com/tps/i4/TB1pgxYJXXXXXcAXpXXrVZt0FXX-640-200.jpg_q50.jpg"); 44 | 45 | headLines = new ArrayList<>(); 46 | headLines.add("Github:ProviderAdapter"); 47 | headLines.add("Author:Victory-Over"); 48 | headLines.add("Email:466708987@qq.com"); 49 | 50 | guessList=new ArrayList<>(); 51 | guessList.add(new GuessData("全棉韩版田园碎花床裙四件套公主风蕾丝花边被套纯棉1.8m床上用品", "https://img.alicdn.com/bao/uploaded/i2/2171322350/TB2SqaTaNaJ.eBjSsziXXaJ_XXa_!!2171322350.jpg_250x250.jpg", 24, 23423)); 52 | guessList.add(new GuessData("2017免烫春秋男士英伦深灰色修身职业商务正装西服套装潮男西装", "https://img.alicdn.com/bao/uploaded/i4/TB1Ke9CJpXXXXa9XpXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 236, 7357)); 53 | guessList.add(new GuessData("现货 Huawei/华为 Mate 9 Pro全网通银钻灰 保时捷", "https://asearch.alicdn.com/bao/uploaded/i2/159530341092325317/TB2mMMbaHBnpuFjSZFGXXX51pXa_!!0-saturn_solar.jpg_250x250.jpg", 43534, 746436)); 54 | guessList.add(new GuessData("各品牌平板修理电脑ipad维修安卓mid刷机升级固件芯片维修换屏", "https://img.alicdn.com/bao/uploaded/i3/20821802/TB2S7E9abVkpuFjSspcXXbSMVXa_!!20821802.jpg_250x250.jpg", 89, 156950)); 55 | guessList.add(new GuessData("代写文章工作总结自我介绍竞聘演讲简报心得体会证婚词调查问卷", "https://img.alicdn.com/bao/uploaded/i1/1846357812/TB21uctkpXXXXX4XpXXXXXXXXXX_!!1846357812.jpg_250x250.jpg", 35, 8769)); 56 | guessList.add(new GuessData("Mate9Pro现货Huawei/华为 Mate 9 Pro 6GB+128GB全网通保时捷现货", "https://img.alicdn.com/bao/uploaded/i2/TB1UypnPVXXXXXzXFXXXXXXXXXX_!!2-item_pic.png_250x250.jpg", 979, 2634634)); 57 | guessList.add(new GuessData("莱虎秋季新款时尚休闲男士西服马夹韩版修身西装马甲背心男英伦潮", "https://img.alicdn.com/bao/uploaded/i2/TB1cA.aPFXXXXc2XFXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 27, 3473436)); 58 | guessList.add(new GuessData("SXT 工作人员购买专区", "https://img.alicdn.com/bao/uploaded/i7/TB10A15PXXXXXa5XVXXYXGcGpXX_M2.SS2_250x250.jpg", 23523, 150)); 59 | guessList.add(new GuessData("梦蔻 全棉蕾丝床笠 韩式花边布艺床裙床罩 床单 1.51.8米 多花色", "https://img.alicdn.com/bao/uploaded/i4/TB1ZQjAJFXXXXXEXFXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 26, 7346443)); 60 | guessList.add(new GuessData("三星c5正品Samsung/三星 Galaxy C5 SM-C5000 C7000 全网通4G手机", "https://img.alicdn.com/bao/uploaded/i1/TB1sDqKLXXXXXanXXXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 2634, 34637)); 61 | guessList.add(new GuessData("全棉韩版田园碎花床裙四件套公主风蕾丝花边被套纯棉1.8m床上用品", "https://img.alicdn.com/bao/uploaded/i2/2171322350/TB2SqaTaNaJ.eBjSsziXXaJ_XXa_!!2171322350.jpg_250x250.jpg", 24, 23423)); 62 | guessList.add(new GuessData("2017免烫春秋男士英伦深灰色修身职业商务正装西服套装潮男西装", "https://img.alicdn.com/bao/uploaded/i4/TB1Ke9CJpXXXXa9XpXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 236, 7357)); 63 | guessList.add(new GuessData("现货 Huawei/华为 Mate 9 Pro全网通银钻灰 保时捷", "https://asearch.alicdn.com/bao/uploaded/i2/159530341092325317/TB2mMMbaHBnpuFjSZFGXXX51pXa_!!0-saturn_solar.jpg_250x250.jpg", 43534, 746436)); 64 | guessList.add(new GuessData("各品牌平板修理电脑ipad维修安卓mid刷机升级固件芯片维修换屏", "https://img.alicdn.com/bao/uploaded/i3/20821802/TB2S7E9abVkpuFjSspcXXbSMVXa_!!20821802.jpg_250x250.jpg", 89, 156950)); 65 | guessList.add(new GuessData("代写文章工作总结自我介绍竞聘演讲简报心得体会证婚词调查问卷", "https://img.alicdn.com/bao/uploaded/i1/1846357812/TB21uctkpXXXXX4XpXXXXXXXXXX_!!1846357812.jpg_250x250.jpg", 35, 8769)); 66 | guessList.add(new GuessData("Mate9Pro现货Huawei/华为 Mate 9 Pro 6GB+128GB全网通保时捷现货", "https://img.alicdn.com/bao/uploaded/i2/TB1UypnPVXXXXXzXFXXXXXXXXXX_!!2-item_pic.png_250x250.jpg", 979, 2634634)); 67 | guessList.add(new GuessData("莱虎秋季新款时尚休闲男士西服马夹韩版修身西装马甲背心男英伦潮", "https://img.alicdn.com/bao/uploaded/i2/TB1cA.aPFXXXXc2XFXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 27, 3473436)); 68 | guessList.add(new GuessData("SXT 工作人员购买专区", "https://img.alicdn.com/bao/uploaded/i7/TB10A15PXXXXXa5XVXXYXGcGpXX_M2.SS2_250x250.jpg", 23523, 150)); 69 | guessList.add(new GuessData("梦蔻 全棉蕾丝床笠 韩式花边布艺床裙床罩 床单 1.51.8米 多花色", "https://img.alicdn.com/bao/uploaded/i4/TB1ZQjAJFXXXXXEXFXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 26, 7346443)); 70 | guessList.add(new GuessData("三星c5正品Samsung/三星 Galaxy C5 SM-C5000 C7000 全网通4G手机", "https://img.alicdn.com/bao/uploaded/i1/TB1sDqKLXXXXXanXXXXXXXXXXXX_!!0-item_pic.jpg_250x250.jpg", 2634, 34637)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.ListView; 6 | 7 | import com.fanneng.android.adapter.provider.IItemBean; 8 | import com.fanneng.android.adapter.provider.IViewProvider; 9 | import com.fanneng.android.adapter.provider.ProviderAdapter; 10 | import com.fanneng.android.adapter.template.action.ActionData; 11 | import com.fanneng.android.adapter.template.action.ActionsProvider; 12 | import com.fanneng.android.adapter.template.banner.BannerData; 13 | import com.fanneng.android.adapter.template.banner.BannerProvider; 14 | import com.fanneng.android.adapter.template.guess.GuessProvider; 15 | import com.fanneng.android.adapter.template.headline.HeadLineData; 16 | import com.fanneng.android.adapter.template.headline.HeadLineProvider; 17 | import com.fanneng.android.adapter.template.recommend.RecommendData; 18 | import com.fanneng.android.adapter.template.recommend.RecommendProvider; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * describe: 25 | * 26 | * @author :鲁宇峰 on 2018/9/13 11:23 27 | * email:luyufengc@enn.cn 28 | */ 29 | public class MainActivity extends AppCompatActivity { 30 | 31 | private ProviderAdapter mAdapter; 32 | private List mDataList; 33 | private MainPageData mainPageData; 34 | private ListView lv_main; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_main); 40 | lv_main = findViewById(R.id.lv_main); 41 | initData(); 42 | 43 | List> providers = new ArrayList<>(); 44 | //将所有模板添加到ProviderAdapter 会根据添加进的模板进行ViewType判断 45 | providers.add(BannerProvider.class); 46 | providers.add(ActionsProvider.class); 47 | providers.add(HeadLineProvider.class); 48 | providers.add(RecommendProvider.class); 49 | providers.add(GuessProvider.class); 50 | 51 | mAdapter = new ProviderAdapter(this, providers); 52 | mDataList = new ArrayList<>(); 53 | //添加banner数据 54 | mDataList.add(mainPageData.getBannerData()); 55 | 56 | //添加功能分类数据 57 | mDataList.add(mainPageData.getActionData()); 58 | 59 | //添加淘宝头条数据 60 | mDataList.add(mainPageData.getHeadLineData()); 61 | 62 | //添加推荐数据 63 | mDataList.add(mainPageData.getRecommendData()); 64 | 65 | //添加猜你喜欢数据 66 | mDataList.addAll(mainPageData.getGuessDataList()); 67 | 68 | mAdapter.setItemBeanList(mDataList); 69 | lv_main.setAdapter(mAdapter); 70 | } 71 | 72 | private void initData() { 73 | mainPageData = new MainPageData(); 74 | 75 | //模拟轮播图数据 76 | BannerData bannerData = new BannerData(); 77 | bannerData.setBanners(Data.bannerList); 78 | mainPageData.setBannerData(bannerData); 79 | 80 | 81 | //模拟功能分类数据 82 | ActionData actionData = new ActionData(); 83 | List actions = new ArrayList<>(); 84 | for (int i = 0; i < 10; i++) { 85 | ActionData.Action action = new ActionData.Action(); 86 | action.setTitle(i < Data.titles.length ? Data.titles[i] : "新增功能" + i); 87 | action.setUrl(i < Data.iconUrl.length ? Data.iconUrl[i] : Data.iconUrl[0]); 88 | actions.add(action); 89 | } 90 | actionData.setAction(actions); 91 | mainPageData.setActionData(actionData); 92 | 93 | //模拟淘宝头条数据 94 | HeadLineData headLineData = new HeadLineData(); 95 | headLineData.setHeadLines(Data.headLines); 96 | mainPageData.setHeadLineData(headLineData); 97 | 98 | //模拟推荐数据 99 | RecommendData recommendData = new RecommendData(); 100 | recommendData.setImgUrl(Data.bannerList.get(Data.bannerList.size() - 2)); 101 | mainPageData.setRecommendData(recommendData); 102 | 103 | //模拟猜你喜欢数据 104 | mainPageData.setGuessDataList(Data.guessList); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/MainPageData.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter; 2 | 3 | import com.fanneng.android.adapter.template.action.ActionData; 4 | import com.fanneng.android.adapter.template.banner.BannerData; 5 | import com.fanneng.android.adapter.template.guess.GuessData; 6 | import com.fanneng.android.adapter.template.headline.HeadLineData; 7 | import com.fanneng.android.adapter.template.recommend.RecommendData; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * describe: 13 | * 14 | * @author :鲁宇峰 on 2018/9/19 14:26 15 | * email:luyufengc@enn.cn 16 | */ 17 | public class MainPageData { 18 | private BannerData bannerData; 19 | 20 | private ActionData actionData; 21 | 22 | private HeadLineData headLineData; 23 | 24 | private RecommendData recommendData; 25 | 26 | private List guessDataList; 27 | 28 | public BannerData getBannerData() { 29 | return bannerData; 30 | } 31 | 32 | public void setBannerData(BannerData bannerData) { 33 | this.bannerData = bannerData; 34 | } 35 | 36 | public ActionData getActionData() { 37 | return actionData; 38 | } 39 | 40 | public void setActionData(ActionData actionData) { 41 | this.actionData = actionData; 42 | } 43 | 44 | public HeadLineData getHeadLineData() { 45 | return headLineData; 46 | } 47 | 48 | public void setHeadLineData(HeadLineData headLineData) { 49 | this.headLineData = headLineData; 50 | } 51 | 52 | public RecommendData getRecommendData() { 53 | return recommendData; 54 | } 55 | 56 | public void setRecommendData(RecommendData recommendData) { 57 | this.recommendData = recommendData; 58 | } 59 | 60 | public List getGuessDataList() { 61 | return guessDataList; 62 | } 63 | 64 | public void setGuessDataList(List guessDataList) { 65 | this.guessDataList = guessDataList; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/action/ActionCardView.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.action; 2 | 3 | import android.content.Context; 4 | import android.support.v4.content.ContextCompat; 5 | import android.util.AttributeSet; 6 | import android.view.Gravity; 7 | import android.view.View; 8 | import android.widget.ImageView; 9 | import android.widget.LinearLayout; 10 | import android.widget.RelativeLayout; 11 | import android.widget.TextView; 12 | import android.widget.Toast; 13 | 14 | import com.bumptech.glide.Glide; 15 | import com.fanneng.android.adapter.R; 16 | import com.fanneng.android.adapter.utils.GlideRoundTransform; 17 | import com.fanneng.android.adapter.utils.UIUtils; 18 | 19 | /** 20 | * describe: 21 | * 22 | * @author :鲁宇峰 on 2018/9/18 16:29 23 | * email:luyufengc@enn.cn 24 | */ 25 | public class ActionCardView extends RelativeLayout implements View.OnClickListener { 26 | private TextView mTitleView; 27 | private ImageView mImageView; 28 | private ActionData.Action mAction; 29 | private Context context; 30 | 31 | public ActionCardView(Context context) { 32 | this(context, null); 33 | } 34 | 35 | public ActionCardView(Context context, AttributeSet attrs) { 36 | this(context, attrs, -1); 37 | } 38 | 39 | public ActionCardView(Context context, AttributeSet attrs, int defStyleAttr) { 40 | super(context, attrs, defStyleAttr); 41 | this.context = context; 42 | init(context); 43 | } 44 | 45 | private void init(Context context) { 46 | LinearLayout linearLayout = new LinearLayout(context); 47 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); 48 | params.gravity = Gravity.CENTER; 49 | linearLayout.setLayoutParams(params); 50 | linearLayout.setOrientation(LinearLayout.VERTICAL); 51 | 52 | mImageView = new ImageView(context); 53 | LinearLayout.LayoutParams imageParams = new LinearLayout.LayoutParams(UIUtils.dp2px(context, 45), UIUtils.dp2px(context, 45)); 54 | imageParams.gravity = Gravity.CENTER; 55 | linearLayout.addView(mImageView, imageParams); 56 | 57 | mTitleView = new TextView(context); 58 | LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); 59 | textParams.topMargin = UIUtils.dp2px(context, 3); 60 | mTitleView.setGravity(Gravity.CENTER); 61 | mTitleView.setLayoutParams(textParams); 62 | mTitleView.setTextColor(ContextCompat.getColor(context, R.color.color_black)); 63 | mTitleView.setTextSize(10); 64 | linearLayout.addView(mTitleView); 65 | 66 | addView(linearLayout); 67 | } 68 | 69 | public void setData(ActionData.Action action) { 70 | mAction = action; 71 | if (action != null) { 72 | mTitleView.setText(action.getTitle()); 73 | //圆角图片 74 | Glide.with(context).load(action.getUrl()).transform(new GlideRoundTransform(context, 30)).into(mImageView); 75 | setOnClickListener(this); 76 | } 77 | } 78 | 79 | @Override 80 | public void onClick(View v) { 81 | if (mAction != null) { 82 | Toast.makeText(context, mAction.getTitle() + "被点击了", Toast.LENGTH_SHORT).show(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/action/ActionData.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.action; 2 | 3 | 4 | import com.fanneng.android.adapter.provider.IItemBean; 5 | import com.fanneng.android.adapter.provider.IViewProvider; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * describe: 11 | * 12 | * @author :鲁宇峰 on 2018/9/18 15:33 13 | * email:luyufengc@enn.cn 14 | */ 15 | public class ActionData implements IItemBean { 16 | private List actions; 17 | 18 | public List getAction() { 19 | return actions; 20 | } 21 | 22 | public void setAction(List actions) { 23 | this.actions = actions; 24 | } 25 | 26 | public static class Action { 27 | private String title; 28 | private String url; 29 | 30 | 31 | public String getTitle() { 32 | return title; 33 | } 34 | 35 | public void setTitle(String title) { 36 | this.title = title; 37 | } 38 | 39 | 40 | public String getUrl() { 41 | return url; 42 | } 43 | 44 | public void setUrl(String url) { 45 | this.url = url; 46 | } 47 | } 48 | 49 | 50 | @Override 51 | public Class getViewProviderClass() { 52 | return ActionsProvider.class; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/action/ActionsProvider.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.action; 2 | 3 | import android.content.Context; 4 | import android.view.Gravity; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.widget.LinearLayout; 8 | 9 | import com.fanneng.android.adapter.provider.IViewProvider; 10 | import com.fanneng.android.adapter.utils.UIUtils; 11 | 12 | import java.util.ArrayList; 13 | 14 | /** 15 | * describe: 16 | * 17 | * @author :鲁宇峰 on 2018/9/19 19:29 18 | * email:luyufengc@enn.cn 19 | */ 20 | public class ActionsProvider implements IViewProvider { 21 | 22 | @Override 23 | public View getItemView(Context context, View convertView, LayoutInflater inflater, Object data, int position) { 24 | HomeActionsHolder holder = null; 25 | if (convertView == null) { 26 | holder = new HomeActionsHolder(); 27 | convertView = createActionsView(context, (ActionData) data, holder); 28 | convertView.setTag(holder); 29 | } else { 30 | holder = (HomeActionsHolder) convertView.getTag(); 31 | } 32 | return convertView; 33 | } 34 | 35 | private View createActionsView(Context context, ActionData data, HomeActionsHolder holder) { 36 | //纵向的线性父布局,用于动态添加横向线性布局 37 | LinearLayout verticalLayout = new LinearLayout(context); 38 | LinearLayout.LayoutParams verticalParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT); 39 | verticalLayout.setOrientation(LinearLayout.VERTICAL); 40 | verticalLayout.setLayoutParams(verticalParams); 41 | 42 | 43 | int size = data.getAction().size(); 44 | 45 | //横向线性布局的属性 46 | LinearLayout.LayoutParams horizontalParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); 47 | horizontalParams.topMargin = UIUtils.dp2px(context, 15); 48 | 49 | //设置每个cardView的params 50 | LinearLayout.LayoutParams cardViewParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); 51 | cardViewParams.gravity = Gravity.CENTER; 52 | cardViewParams.weight = 1; 53 | 54 | 55 | //size>5 则需要换行 56 | for (int j = 0; j < size / 5 + 1; j++) { 57 | //横向的线性布局,用于包装所有功能分类 58 | LinearLayout horizontalLayout = new LinearLayout(context); 59 | horizontalLayout.setLayoutParams(horizontalParams); 60 | horizontalLayout.setOrientation(LinearLayout.HORIZONTAL); 61 | //每行5个功能分类 62 | horizontalLayout.setWeightSum(5); 63 | 64 | for (int i = j * 5; i < j * 5 + 5; i++) { 65 | //防止越界 66 | if (i >= size) { 67 | break; 68 | } 69 | ActionCardView cardView = null; 70 | //先取缓存中的view 71 | if (holder.views.size() > i) { 72 | cardView = holder.views.get(i); 73 | } 74 | 75 | //缓存策略 76 | if (cardView == null) { 77 | cardView = new ActionCardView(context); 78 | cardView.setData(data.getAction().get(i)); 79 | horizontalLayout.addView(cardView, cardViewParams); 80 | holder.views.add(cardView); 81 | } else { 82 | cardView.setData(data.getAction().get(i)); 83 | } 84 | 85 | } 86 | 87 | verticalLayout.addView(horizontalLayout); 88 | } 89 | 90 | 91 | // //创建父布局将功能列表和分割线添加进去 92 | // FrameLayout container = new FrameLayout(context); 93 | // addDivider(container, context); 94 | // container.addView(verticalLayout); 95 | 96 | return verticalLayout; 97 | } 98 | 99 | // private void addDivider(FrameLayout container, Context context) { 100 | // View divider = new View(context); 101 | // divider.setBackgroundColor(context.getResources().getColor(R.color.color_eeeeee)); 102 | // FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 1); 103 | // params.leftMargin = UIUtils.dp2px(context, 15); 104 | // params.rightMargin = params.leftMargin; 105 | // params.bottomMargin = UIUtils.dp2px(context, 8); 106 | // params.gravity = Gravity.BOTTOM; 107 | // container.addView(divider, params); 108 | // } 109 | 110 | private static class HomeActionsHolder { 111 | public ArrayList views = new ArrayList(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/banner/BannerData.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.banner; 2 | 3 | 4 | import com.fanneng.android.adapter.provider.IItemBean; 5 | import com.fanneng.android.adapter.provider.IViewProvider; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * describe: 11 | * 12 | * @author :鲁宇峰 on 2018/9/19 10:22 13 | * email:luyufengc@enn.cn 14 | */ 15 | public class BannerData implements IItemBean { 16 | 17 | private List banners; 18 | 19 | public List getBanners() { 20 | return banners; 21 | } 22 | 23 | public void setBanners(List banners) { 24 | this.banners = banners; 25 | } 26 | 27 | @Override 28 | public Class getViewProviderClass() { 29 | return BannerProvider.class; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/banner/BannerProvider.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.banner; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | 7 | import com.fanneng.android.adapter.R; 8 | import com.fanneng.android.adapter.provider.IViewProvider; 9 | import com.fanneng.android.adapter.utils.GlideImageLoader; 10 | import com.youth.banner.Banner; 11 | 12 | /** 13 | * describe:Banner图模板 14 | * 15 | * @author :鲁宇峰 on 2018/9/19 10:59 16 | * email:luyufengc@enn.cn 17 | */ 18 | public class BannerProvider implements IViewProvider { 19 | private Context mContext; 20 | private boolean isInit = true; 21 | 22 | @Override 23 | public View getItemView(Context context, View convertView, LayoutInflater inflater, Object data, int position) { 24 | ViewHolder holder; 25 | if (convertView == null) { 26 | convertView = inflater.inflate(R.layout.template_slideshow, null); 27 | holder = new ViewHolder(convertView); 28 | convertView.setTag(holder); 29 | isInit = true; 30 | } else { 31 | holder = (ViewHolder) convertView.getTag(); 32 | } 33 | mContext = context; 34 | BannerData bannerData = (BannerData) data; 35 | //由于此缓存控件只需要赋值一次值,所以只需要初始化的时候bind一次就行了 36 | if (isInit) { 37 | bindData(holder, bannerData); 38 | isInit = false; 39 | } 40 | return convertView; 41 | } 42 | 43 | private void bindData(ViewHolder holder, BannerData bannerData) { 44 | if (mContext == null) return; 45 | if (bannerData != null && bannerData.getBanners().size() > 0) { 46 | holder.banner.setImageLoader(new GlideImageLoader()); 47 | holder.banner.setImages(bannerData.getBanners()); 48 | holder.banner.start(); 49 | } else { 50 | holder.banner.setVisibility(View.GONE); 51 | } 52 | } 53 | 54 | 55 | private class ViewHolder { 56 | Banner banner; 57 | 58 | ViewHolder(View view) { 59 | banner = view.findViewById(R.id.banner); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/guess/GuessData.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.guess; 2 | 3 | 4 | import com.fanneng.android.adapter.provider.IItemBean; 5 | import com.fanneng.android.adapter.provider.IViewProvider; 6 | 7 | /** 8 | * describe: 9 | * 10 | * @author :鲁宇峰 on 2018/9/21 10:29 11 | * email:luyufengc@enn.cn 12 | */ 13 | public class GuessData implements IItemBean { 14 | private String name; 15 | private String url; 16 | private double price; 17 | private int count; 18 | 19 | public GuessData(String name, String url, double price, int count) { 20 | this.name = name; 21 | this.url = url; 22 | this.price = price; 23 | this.count = count; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getUrl() { 35 | return url; 36 | } 37 | 38 | public void setUrl(String url) { 39 | this.url = url; 40 | } 41 | 42 | public double getPrice() { 43 | return price; 44 | } 45 | 46 | public void setPrice(double price) { 47 | this.price = price; 48 | } 49 | 50 | public int getCount() { 51 | return count; 52 | } 53 | 54 | public void setCount(int count) { 55 | this.count = count; 56 | } 57 | 58 | @Override 59 | public Class getViewProviderClass() { 60 | return GuessProvider.class; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/guess/GuessProvider.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.guess; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | 9 | import com.bumptech.glide.Glide; 10 | import com.fanneng.android.adapter.R; 11 | import com.fanneng.android.adapter.provider.IViewProvider; 12 | 13 | /** 14 | * describe:猜你喜欢模板 15 | * 16 | * @author :鲁宇峰 on 2018/9/21 11:04 17 | * email:luyufengc@enn.cn 18 | */ 19 | public class GuessProvider implements IViewProvider { 20 | private Context mContext; 21 | 22 | @Override 23 | public View getItemView(Context context, View convertView, LayoutInflater inflater, Object data, int position) { 24 | ViewHolder holder; 25 | if (convertView == null) { 26 | convertView = inflater.inflate(R.layout.template_guess, null); 27 | holder = new ViewHolder(convertView); 28 | convertView.setTag(holder); 29 | } else { 30 | holder = (ViewHolder) convertView.getTag(); 31 | } 32 | mContext = context; 33 | GuessData guessData = (GuessData) data; 34 | bindData(holder, guessData); 35 | return convertView; 36 | } 37 | 38 | private void bindData(ViewHolder holder, GuessData guess) { 39 | if (mContext == null) return; 40 | Glide.with(mContext).load(guess.getUrl()).into(holder.iv_home); 41 | holder.tv_title.setText(guess.getName()); 42 | holder.tv_price.setText("¥"+guess.getPrice()); 43 | holder.tv_count.setText(guess.getCount() + "人付款"); 44 | 45 | Glide.with(mContext).load(guess.getUrl()).into(holder.iv_home2); 46 | holder.tv_title2.setText(guess.getName()); 47 | holder.tv_price2.setText("¥"+guess.getPrice()); 48 | holder.tv_count2.setText(guess.getCount() + "人付款"); 49 | } 50 | 51 | 52 | private class ViewHolder { 53 | ImageView iv_home; 54 | TextView tv_title; 55 | TextView tv_price; 56 | TextView tv_count; 57 | ImageView iv_home2; 58 | TextView tv_title2; 59 | TextView tv_price2; 60 | TextView tv_count2; 61 | 62 | ViewHolder(View view) { 63 | iv_home = view.findViewById(R.id.iv_home); 64 | tv_title = view.findViewById(R.id.tv_title); 65 | tv_price = view.findViewById(R.id.tv_price); 66 | tv_count = view.findViewById(R.id.tv_count); 67 | iv_home2 = view.findViewById(R.id.iv_home2); 68 | tv_title2 = view.findViewById(R.id.tv_title2); 69 | tv_price2 = view.findViewById(R.id.tv_price2); 70 | tv_count2 = view.findViewById(R.id.tv_count2); 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/headline/HeadLineData.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.headline; 2 | 3 | 4 | import com.fanneng.android.adapter.provider.IItemBean; 5 | import com.fanneng.android.adapter.provider.IViewProvider; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * describe: 11 | * 12 | * @author :鲁宇峰 on 2018/9/20 18:19 13 | * email:luyufengc@enn.cn 14 | */ 15 | public class HeadLineData implements IItemBean { 16 | 17 | private List headLines; 18 | 19 | public List getHeadLines() { 20 | return headLines; 21 | } 22 | 23 | public void setHeadLines(List headLines) { 24 | this.headLines = headLines; 25 | } 26 | 27 | @Override 28 | public Class getViewProviderClass() { 29 | return HeadLineProvider.class; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/headline/HeadLineProvider.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.headline; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.fanneng.android.adapter.R; 9 | import com.fanneng.android.adapter.provider.IViewProvider; 10 | import com.oushangfeng.marqueelayout.MarqueeLayout; 11 | import com.oushangfeng.marqueelayout.MarqueeLayoutAdapter; 12 | 13 | /** 14 | * describe:淘宝头条模板 15 | * 16 | * @author :鲁宇峰 on 2018/9/19 20:03 17 | * email:luyufengc@enn.cn 18 | */ 19 | public class HeadLineProvider implements IViewProvider { 20 | 21 | private Context mContext; 22 | private boolean isInit = true; 23 | 24 | @Override 25 | public View getItemView(Context context, View convertView, LayoutInflater inflater, Object data, int position) { 26 | HeadLineProvider.ViewHolder holder; 27 | if (convertView == null) { 28 | convertView = inflater.inflate(R.layout.template_headline, null); 29 | holder = new HeadLineProvider.ViewHolder(convertView); 30 | convertView.setTag(holder); 31 | isInit = true; 32 | } else { 33 | holder = (HeadLineProvider.ViewHolder) convertView.getTag(); 34 | } 35 | mContext = context; 36 | HeadLineData headLineData = (HeadLineData) data; 37 | if (isInit) { 38 | bindData(holder, headLineData); 39 | isInit = false; 40 | } 41 | return convertView; 42 | } 43 | 44 | private void bindData(HeadLineProvider.ViewHolder holder, HeadLineData headLineData) { 45 | if (mContext == null) return; 46 | if (headLineData != null && headLineData.getHeadLines().size() > 0) { 47 | MarqueeLayoutAdapter topAdapter = new MarqueeLayoutAdapter(headLineData.getHeadLines()) { 48 | @Override 49 | protected int getItemLayoutId() { 50 | return R.layout.template_headline_marquee_item; 51 | } 52 | 53 | @Override 54 | protected void initView(View view, int position, String item) { 55 | ((TextView) view).setText(item); 56 | 57 | } 58 | }; 59 | holder.marquee.setAdapter(topAdapter); 60 | holder.marquee.start(); 61 | } else { 62 | holder.marquee.setVisibility(View.GONE); 63 | } 64 | } 65 | 66 | private class ViewHolder { 67 | MarqueeLayout marquee; 68 | 69 | ViewHolder(View view) { 70 | marquee = view.findViewById(R.id.marquee_layout); 71 | } 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/recommend/RecommendData.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.recommend; 2 | 3 | 4 | import com.fanneng.android.adapter.provider.IItemBean; 5 | import com.fanneng.android.adapter.provider.IViewProvider; 6 | 7 | /** 8 | * describe: 9 | * 10 | * @author :鲁宇峰 on 2018/9/20 17:23 11 | * email:luyufengc@enn.cn 12 | */ 13 | public class RecommendData implements IItemBean { 14 | String imgUrl; 15 | 16 | public String getImgUrl() { 17 | return imgUrl; 18 | } 19 | 20 | public void setImgUrl(String imgUrl) { 21 | this.imgUrl = imgUrl; 22 | } 23 | 24 | @Override 25 | public Class getViewProviderClass() { 26 | return RecommendProvider.class; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/template/recommend/RecommendProvider.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.template.recommend; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.widget.ImageView; 7 | 8 | import com.bumptech.glide.Glide; 9 | import com.fanneng.android.adapter.R; 10 | import com.fanneng.android.adapter.provider.IViewProvider; 11 | import com.fanneng.android.adapter.utils.GlideRoundTransform; 12 | 13 | /** 14 | * describe:推荐模板 15 | * 16 | * @author :鲁宇峰 on 2018/9/20 13:29 17 | * email:luyufengc@enn.cn 18 | */ 19 | public class RecommendProvider implements IViewProvider { 20 | private Context mContext; 21 | private boolean isInit = true; 22 | 23 | @Override 24 | public View getItemView(Context context, View convertView, LayoutInflater inflater, Object data, int position) { 25 | ViewHolder holder; 26 | if (convertView == null) { 27 | convertView = inflater.inflate(R.layout.template_recommend, null); 28 | holder = new ViewHolder(convertView); 29 | convertView.setTag(holder); 30 | isInit = true; 31 | } else { 32 | holder = (ViewHolder) convertView.getTag(); 33 | } 34 | mContext = context; 35 | RecommendData recommendData = (RecommendData) data; 36 | if (isInit) { 37 | bindData(holder, recommendData); 38 | isInit = false; 39 | } 40 | return convertView; 41 | } 42 | 43 | private void bindData(ViewHolder holder, RecommendData recommendData) { 44 | if (mContext == null) return; 45 | if (recommendData != null) { 46 | Glide.with(mContext).load(recommendData.getImgUrl()).transform(new GlideRoundTransform(mContext, 10)).into(holder.ivRecommend); 47 | } else { 48 | holder.ivRecommend.setVisibility(View.GONE); 49 | } 50 | } 51 | 52 | 53 | private class ViewHolder { 54 | ImageView ivRecommend; 55 | 56 | ViewHolder(View view) { 57 | ivRecommend = view.findViewById(R.id.iv_recommend); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/utils/GlideImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.utils; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.bumptech.glide.Glide; 7 | import com.youth.banner.loader.ImageLoader; 8 | 9 | /** 10 | * describe: 11 | * 12 | * @author :鲁宇峰 on 2018/9/19 14:06 13 | * email:luyufengc@enn.cn 14 | */ 15 | public class GlideImageLoader extends ImageLoader { 16 | @Override 17 | public void displayImage(Context context, Object path, ImageView imageView) { 18 | //Glide 加载图片简单用法 19 | Glide.with(context).load(path).into(imageView); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/utils/GlideRoundTransform.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.utils; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapShader; 7 | import android.graphics.Canvas; 8 | import android.graphics.Paint; 9 | import android.graphics.RectF; 10 | 11 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 12 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 13 | 14 | /** 15 | * describe: 16 | * 17 | * @author :鲁宇峰 on 2018/9/19 19:45 18 | * email:luyufengc@enn.cn 19 | */ 20 | public class GlideRoundTransform extends BitmapTransformation { 21 | 22 | private static float radius = 0f; 23 | 24 | public GlideRoundTransform(Context context) { 25 | this(context, 4); 26 | } 27 | 28 | public GlideRoundTransform(Context context, int dp) { 29 | super(context); 30 | radius = Resources.getSystem().getDisplayMetrics().density * dp; 31 | } 32 | 33 | @Override 34 | protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 35 | return roundCrop(pool, toTransform); 36 | } 37 | 38 | private static Bitmap roundCrop(BitmapPool pool, Bitmap source) { 39 | if (source == null) return null; 40 | 41 | Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); 42 | if (result == null) { 43 | result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); 44 | } 45 | 46 | Canvas canvas = new Canvas(result); 47 | Paint paint = new Paint(); 48 | paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 49 | paint.setAntiAlias(true); 50 | RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight()); 51 | canvas.drawRoundRect(rectF, radius, radius, paint); 52 | return result; 53 | } 54 | 55 | @Override 56 | public String getId() { 57 | return getClass().getName() + Math.round(radius); 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/fanneng/android/adapter/utils/UIUtils.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter.utils; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * describe: 7 | * 8 | * @author :鲁宇峰 on 2018/9/19 16:29 9 | * email:luyufengc@enn.cn 10 | */ 11 | public class UIUtils { 12 | 13 | /** 14 | * dp转换成px 15 | */ 16 | public static int dp2px(Context context, float dpValue) { 17 | float scale = context.getResources().getDisplayMetrics().density; 18 | return (int) (dpValue * scale + 0.5f); 19 | } 20 | 21 | /** 22 | * px转换成dp 23 | */ 24 | public static int px2dp(Context context, float pxValue) { 25 | float scale = context.getResources().getDisplayMetrics().density; 26 | return (int) (pxValue / scale + 0.5f); 27 | } 28 | 29 | /** 30 | * sp转换成px 31 | */ 32 | public static int sp2px(Context context, float spValue) { 33 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 34 | return (int) (spValue * fontScale + 0.5f); 35 | } 36 | 37 | /** 38 | * px转换成sp 39 | */ 40 | public static int px2sp(Context context, float pxValue) { 41 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 42 | return (int) (pxValue / fontScale + 0.5f); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 15 | 16 | 23 | 24 | 32 | 33 | 43 | 44 | 47 | 48 | 49 | 59 | 60 | 68 | 69 | 70 | 71 | 72 | 77 | 78 | 79 | 80 | 87 | 88 | 95 | 96 | 97 | 98 | 99 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/template_action_cardview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/template_guess.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 18 | 19 | 30 | 31 | 37 | 38 | 44 | 45 | 52 | 53 | 54 | 55 | 56 | 62 | 63 | 67 | 68 | 79 | 80 | 86 | 87 | 93 | 94 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /app/src/main/res/layout/template_headline.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 14 | 15 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/template_headline_marquee_item.xml: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/template_recommend.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/template_slideshow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/home_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/home_camera.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/home_msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/home_msg.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/home_scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/home_scan.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/home_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/home_search.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_headline.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/icon_headline.jpeg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/icon_recommend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xhdpi/icon_recommend.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /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 | 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 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF5000 4 | #AF3600 5 | #1DA1F2 6 | 7 | #141414 8 | #666666 9 | #FF686C 10 | #FF8E00 11 | 12 | #F0F0F0 13 | #CCC 14 | 15 | #C0C0C0 16 | 17 | #eeeeee 18 | #000000 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ProviderAdapter 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/vpi_defaults.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | #FFFFFFFF 6 | #00000000 7 | 0 8 | 3dp 9 | false 10 | #FFDDDDDD 11 | 1dp 12 | 13 | 12dp 14 | 4dp 15 | 1dp 16 | #FF33B5E5 17 | #FFBBBBBB 18 | true 19 | 20 | 4dp 21 | #FF33B5E5 22 | 2dp 23 | 2 24 | 4dp 25 | 20dp 26 | 7dp 27 | 0 28 | #FFFFFFFF 29 | true 30 | #BBFFFFFF 31 | 15dp 32 | 5dp 33 | 7dp 34 | 35 | true 36 | 300 37 | 400 38 | #FF33B5E5 39 | 40 | -------------------------------------------------------------------------------- /app/src/test/java/com/fanneng/android/adapter/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.fanneng.android.adapter; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.1' 11 | 12 | 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 | mavenCentral() 21 | google() 22 | jcenter() 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 10 14:03:46 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /provideradapter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Victory-Over/ProviderAdapter/828ef1392dc09c6c289c65dc3c0e02583462c92a/provideradapter.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':adapter' 2 | --------------------------------------------------------------------------------