├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── administrator │ │ └── test │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── administrator │ │ │ └── test │ │ │ ├── ColorFilterImageView.java │ │ │ ├── DisplayUtils.java │ │ │ ├── ImageUrls.java │ │ │ ├── MainActivity.java │ │ │ ├── ManyMultiActivity.java │ │ │ ├── MultiImageView.java │ │ │ └── recyclerview │ │ │ ├── BaseRecyclerAdapter.java │ │ │ ├── BaseRecyclerViewHolder.java │ │ │ ├── ChatRoomMsgBean.java │ │ │ └── CustomScrollView.java │ └── res │ │ ├── drawable │ │ └── default_image.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_many_multi.xml │ │ └── item_imageview.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── administrator │ └── test │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img └── a.jpg └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.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/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultipleImageView 2 | [我的博客,详细介绍使用方法](http://blog.csdn.net/sw5131899/article/details/52838261) 3 | 效果图 4 | 5 | ![](https://github.com/SingleShu/MultipleImageView/raw/master/img/a.jpg) 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.example.administrator.test" 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | testCompile 'junit:junit:4.12' 29 | compile 'com.github.bumptech.glide:glide:3.7.0' 30 | compile 'com.android.support:recyclerview-v7:24.2.0' 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/administrator/test/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test; 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 | * Instrumentation 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.example.administrator.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/ColorFilterImageView.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.PorterDuff.Mode; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.View; 9 | import android.view.View.OnTouchListener; 10 | import android.widget.ImageView; 11 | 12 | /** 13 | * @author hnclca 14 | * @ClassName: ColorFilterImageView 15 | * @Description: 实现图像根据按下抬起动作变化颜色 16 | * @date 2016-02-26 17 | */ 18 | public class ColorFilterImageView extends ImageView implements OnTouchListener { 19 | public ColorFilterImageView(Context context) { 20 | this(context, null, 0); 21 | } 22 | 23 | public ColorFilterImageView(Context context, AttributeSet attrs) { 24 | this(context, attrs, 0); 25 | } 26 | 27 | public ColorFilterImageView(Context context, AttributeSet attrs, int defStyle) { 28 | super(context, attrs, defStyle); 29 | init(); 30 | } 31 | 32 | private void init() { 33 | setOnTouchListener(this); 34 | } 35 | 36 | @Override 37 | public boolean onTouch(View v, MotionEvent event) { 38 | switch (event.getAction()) { 39 | case MotionEvent.ACTION_DOWN: // 按下时图像变灰 40 | setColorFilter(Color.GRAY, Mode.MULTIPLY); 41 | break; 42 | case MotionEvent.ACTION_UP: // 手指离开或取消操作时恢复原色 43 | case MotionEvent.ACTION_CANCEL: 44 | setColorFilter(Color.TRANSPARENT); 45 | break; 46 | default: 47 | break; 48 | } 49 | return false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/DisplayUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 venshine.cn@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 | package com.example.administrator.test; 17 | 18 | import android.app.Activity; 19 | import android.content.Context; 20 | import android.content.res.Configuration; 21 | import android.graphics.Rect; 22 | import android.util.DisplayMetrics; 23 | import android.view.Window; 24 | 25 | /** 26 | * 屏幕显示相关信息 27 | * 28 | * @author venshine 29 | */ 30 | public class DisplayUtils { 31 | 32 | /** 33 | * 是否横屏 34 | * 35 | * @param context 36 | * @return 37 | */ 38 | public static boolean isLandscape(Context context) { 39 | return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; 40 | } 41 | 42 | /** 43 | * 是否竖屏 44 | * 45 | * @param context 46 | * @return 47 | */ 48 | public static boolean isPortrait(Context context) { 49 | return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; 50 | } 51 | 52 | /** 53 | * Get screen width, in pixels 54 | * 55 | * @param context 56 | * @return 57 | */ 58 | public static int getScreenWidth(Context context) { 59 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 60 | return dm.widthPixels; 61 | } 62 | 63 | /** 64 | * Get screen height, in pixels 65 | * 66 | * @param context 67 | * @return 68 | */ 69 | public static int getScreenHeight(Context context) { 70 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 71 | return dm.heightPixels; 72 | } 73 | 74 | /** 75 | * Get screen density, the logical density of the display 76 | * 77 | * @param context 78 | * @return 79 | */ 80 | public static float getScreenDensity(Context context) { 81 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 82 | return dm.density; 83 | } 84 | 85 | /** 86 | * Get screen density dpi, the screen density expressed as dots-per-inch 87 | * 88 | * @param context 89 | * @return 90 | */ 91 | public static int getScreenDensityDPI(Context context) { 92 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 93 | return dm.densityDpi; 94 | } 95 | 96 | /** 97 | * Get titlebar height, this method cannot be used in onCreate(),onStart(),onResume(), unless it is called in the 98 | * post(Runnable). 99 | * 100 | * @param activity 101 | * @return 102 | */ 103 | public static int getTitleBarHeight(Activity activity) { 104 | int statusBarHeight = getStatusBarHeight(activity); 105 | int contentViewTop = activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop(); 106 | int titleBarHeight = contentViewTop - statusBarHeight; 107 | return titleBarHeight < 0 ? 0 : titleBarHeight; 108 | } 109 | 110 | /** 111 | * Get statusbar height, this method cannot be used in onCreate(),onStart(),onResume(), unless it is called in the 112 | * post(Runnable). 113 | * 114 | * @param activity 115 | * @return 116 | */ 117 | public static int getStatusBarHeight(Activity activity) { 118 | Rect rect = new Rect(); 119 | activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); 120 | return rect.top; 121 | } 122 | 123 | /** 124 | * Get statusbar height 125 | * 126 | * @param activity 127 | * @return 128 | */ 129 | public static int getStatusBarHeight2(Activity activity) { 130 | int statusBarHeight = getStatusBarHeight(activity); 131 | if (0 == statusBarHeight) { 132 | Class localClass; 133 | try { 134 | localClass = Class.forName("com.android.internal.R$dimen"); 135 | Object localObject = localClass.newInstance(); 136 | int id = Integer.parseInt(localClass.getField("status_bar_height").get(localObject).toString()); 137 | statusBarHeight = activity.getResources().getDimensionPixelSize(id); 138 | } catch (ClassNotFoundException e) { 139 | e.printStackTrace(); 140 | } catch (IllegalAccessException e) { 141 | e.printStackTrace(); 142 | } catch (InstantiationException e) { 143 | e.printStackTrace(); 144 | } catch (NumberFormatException e) { 145 | e.printStackTrace(); 146 | } catch (IllegalArgumentException e) { 147 | e.printStackTrace(); 148 | } catch (SecurityException e) { 149 | e.printStackTrace(); 150 | } catch (NoSuchFieldException e) { 151 | e.printStackTrace(); 152 | } 153 | } 154 | return statusBarHeight; 155 | } 156 | 157 | /** 158 | * Convert dp to px by the density of phone 159 | * 160 | * @param context 161 | * @param dp 162 | * @return 163 | */ 164 | public static int dip2px(Context context, float dp) { 165 | if (context == null) { 166 | return -1; 167 | } 168 | return (int) (dipToPx(context, dp) + 0.5f); 169 | } 170 | 171 | /** 172 | * Convert dp to px 173 | * 174 | * @param context 175 | * @param dp 176 | * @return 177 | */ 178 | private static float dipToPx(Context context, float dp) { 179 | if (context == null) { 180 | return -1; 181 | } 182 | float scale = context.getResources().getDisplayMetrics().density; 183 | return dp * scale; 184 | } 185 | 186 | /** 187 | * Convert px to dp by the density of phone 188 | * 189 | * @param context 190 | * @param px 191 | * @return 192 | */ 193 | public static int px2dip(Context context, float px) { 194 | if (context == null) { 195 | return -1; 196 | } 197 | return (int) (pxToDip(context, px) + 0.5f); 198 | } 199 | 200 | /** 201 | * Convert px to dp 202 | * 203 | * @param context 204 | * @param px 205 | * @return 206 | */ 207 | private static float pxToDip(Context context, float px) { 208 | if (context == null) { 209 | return -1; 210 | } 211 | float scale = context.getResources().getDisplayMetrics().density; 212 | return px / scale; 213 | } 214 | 215 | /** 216 | * Convert px to sp 217 | * 218 | * @param context 219 | * @param px 220 | * @return 221 | */ 222 | public static int px2sp(Context context, float px) { 223 | return (int) (pxToSp(context, px) + 0.5f); 224 | } 225 | 226 | /** 227 | * Convert px to sp 228 | * 229 | * @param context 230 | * @param px 231 | * @return 232 | */ 233 | private static float pxToSp(Context context, float px) { 234 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 235 | return px / fontScale; 236 | } 237 | 238 | /** 239 | * Convert sp to px 240 | * 241 | * @param context 242 | * @param sp 243 | * @return 244 | */ 245 | public static int sp2px(Context context, float sp) { 246 | return (int) (spToPx(context, sp) + 0.5f); 247 | } 248 | 249 | /** 250 | * Convert sp to px 251 | * 252 | * @param context 253 | * @param sp 254 | * @return 255 | */ 256 | private static float spToPx(Context context, float sp) { 257 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 258 | return sp * fontScale; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/ImageUrls.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by ShuWen on 2017/5/5. 7 | */ 8 | 9 | public class ImageUrls { 10 | private List imageUrls; 11 | 12 | public List getImageUrls() { 13 | return imageUrls; 14 | } 15 | 16 | public void setImageUrls(List imageUrls) { 17 | this.imageUrls = imageUrls; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class MainActivity extends AppCompatActivity { 12 | 13 | 14 | MultiImageView multiImageView; 15 | MultiImageView multiImageView1; 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_main); 20 | multiImageView = (MultiImageView) findViewById(R.id.multiImage); 21 | 22 | Listimgs = new ArrayList<>(); 23 | imgs.add("http://upload.chinaz.com/2015/1103/1446540614989.jpg"); 24 | imgs.add("http://userimage8.360doc.com/16/0519/19/156649_201605191906460585727460.jpg"); 25 | // imgs.add("http://img.sinmeng.com/2017/01/18/1_151219124049_2.jpg"); 26 | // imgs.add("http://joymepic.joyme.com/article/uploads/20163/111460364908999742.jpeg?imageView2/1"); 27 | // imgs.add("http://joymepic.joyme.com/article/uploads/20163/111460364909699286.jpeg?imageView2/1"); 28 | // imgs.add("http://img4.imgtn.bdimg.com/it/u=2963995585,3532148334&fm=214&gp=0.jpg"); 29 | // imgs.add("http://userimage7.360doc.com/16/0114/19/5247515_201601141943070361263319.jpg"); 30 | // imgs.add("http://userimage8.360doc.com/17/0118/19/156649_201701181902460952150615.jpg"); 31 | 32 | multiImageView.setList(imgs); 33 | 34 | Listimgs1 = new ArrayList<>(); 35 | imgs1.add("http://upload.chinaz.com/2015/1103/1446540614989.jpg"); 36 | imgs1.add("http://userimage8.360doc.com/16/0519/19/156649_201605191906460585727460.jpg"); 37 | imgs1.add("http://img.sinmeng.com/2017/01/18/1_151219124049_2.jpg"); 38 | imgs1.add("http://joymepic.joyme.com/article/uploads/20163/111460364908999742.jpeg?imageView2/1"); 39 | imgs1.add("http://joymepic.joyme.com/article/uploads/20163/111460364909699286.jpeg?imageView2/1"); 40 | 41 | multiImageView1 = (MultiImageView) findViewById(R.id.multiImage1); 42 | multiImageView1.setList(imgs1); 43 | 44 | } 45 | public void click(View view){ 46 | startActivity(new Intent(this,ManyMultiActivity.class)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/ManyMultiActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.support.v7.widget.DefaultItemAnimator; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.Toast; 12 | 13 | import com.example.administrator.test.recyclerview.BaseRecyclerAdapter; 14 | import com.example.administrator.test.recyclerview.BaseRecyclerViewHolder; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | public class ManyMultiActivity extends AppCompatActivity { 20 | 21 | private BaseRecyclerAdapter adapter; 22 | private List manyGrils = new ArrayList<>(); 23 | private RecyclerView recyclerview; 24 | 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_many_multi); 30 | recyclerview = (RecyclerView) findViewById(R.id.recyclerview); 31 | 32 | for (int i = 0; i < 10; i++) { 33 | ImageUrls imageUrls = new ImageUrls(); 34 | Listimgs = new ArrayList<>(); 35 | imgs.add("http://upload.chinaz.com/2015/1103/1446540614989.jpg"); 36 | imgs.add("http://userimage8.360doc.com/16/0519/19/156649_201605191906460585727460.jpg"); 37 | imgs.add("http://img.sinmeng.com/2017/01/18/1_151219124049_2.jpg"); 38 | imgs.add("http://joymepic.joyme.com/article/uploads/20163/111460364908999742.jpeg?imageView2/1"); 39 | imgs.add("http://joymepic.joyme.com/article/uploads/20163/111460364909699286.jpeg?imageView2/1"); 40 | imgs.add("http://img4.imgtn.bdimg.com/it/u=2963995585,3532148334&fm=214&gp=0.jpg"); 41 | imgs.add("http://userimage7.360doc.com/16/0114/19/5247515_201601141943070361263319.jpg"); 42 | imgs.add("http://userimage8.360doc.com/17/0118/19/156649_201701181902460952150615.jpg"); 43 | imageUrls.setImageUrls(imgs); 44 | manyGrils.add(imageUrls); 45 | } 46 | 47 | adapter = new BaseRecyclerAdapter(this,manyGrils) { 48 | @Override 49 | public int getItemLayoutId(int viewType) { 50 | return R.layout.item_imageview; 51 | } 52 | 53 | @Override 54 | public void bindData(BaseRecyclerViewHolder holder, int position, ImageUrls item) { 55 | MultiImageView imageView = (MultiImageView) holder.getView(R.id.item_multiimage); 56 | imageView.setList(item.getImageUrls()); 57 | imageView.setOnItemClickListener(new MultiImageView.OnItemClickListener() { 58 | @Override 59 | public void onItemClick(View view, int position) { 60 | Toast.makeText(ManyMultiActivity.this, "选中了"+position+"号小姐~", Toast.LENGTH_SHORT).show(); 61 | } 62 | }); 63 | } 64 | }; 65 | 66 | recyclerview.setHasFixedSize(true); 67 | recyclerview.setAdapter(adapter); 68 | recyclerview.setLayoutManager(new LinearLayoutManager(this)); 69 | recyclerview.setItemAnimator(new DefaultItemAnimator()); 70 | 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/MultiImageView.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.Gravity; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | import android.widget.RelativeLayout; 12 | import android.widget.TextView; 13 | 14 | 15 | import com.bumptech.glide.Glide; 16 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 17 | 18 | import java.util.List; 19 | 20 | 21 | /** 22 | * @author shoyu 23 | * @ClassName MultiImageView.java 24 | * @Description: 显示1~N张图片的View 25 | */ 26 | 27 | public class MultiImageView extends LinearLayout { 28 | public static int MAX_WIDTH = 0; 29 | 30 | // 照片的Url列表 31 | private List imagesList; 32 | 33 | /** 34 | * 长度 单位为Pixel 35 | **/ 36 | private int pxOneMaxWandH; // 单张图最大允许宽高 37 | private int pxMoreWandH = 0;// 多张图的宽高 38 | private int pxImagePadding = DisplayUtils.dip2px(getContext(), 3);// 图片间的间距 39 | 40 | private int MAX_PER_ROW_COUNT = 3;// 每行显示最大数 41 | 42 | private LayoutParams onePicPara; 43 | private LayoutParams morePara, moreParaColumnFirst; 44 | private LayoutParams rowPara; 45 | 46 | private OnItemClickListener mOnItemClickListener; 47 | 48 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 49 | mOnItemClickListener = onItemClickListener; 50 | } 51 | 52 | public MultiImageView(Context context) { 53 | super(context); 54 | Log.i("tag00","MultiImageView(Context context)"); 55 | 56 | } 57 | 58 | public MultiImageView(Context context, AttributeSet attrs) { 59 | super(context, attrs); 60 | Log.i("tag00","MultiImageView(Context context, AttributeSet attrs)"); 61 | 62 | } 63 | 64 | public void setList(List lists) throws IllegalArgumentException { 65 | Log.i("tag00","setList(List lists)" + lists.size()); 66 | 67 | if (lists == null) { 68 | throw new IllegalArgumentException("imageList is null..."); 69 | } 70 | imagesList = lists; 71 | 72 | if (MAX_WIDTH > 0) { 73 | pxMoreWandH = (MAX_WIDTH - pxImagePadding * 2) / 3; //解决右侧图片和内容对不齐问题 74 | pxOneMaxWandH = MAX_WIDTH * 2 / 3; 75 | initImageLayoutParams(); 76 | } 77 | 78 | initView(); 79 | } 80 | 81 | @Override 82 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 83 | Log.i("tag00","onMeasure() widthMeasureSpec:"+widthMeasureSpec+" heightMeasureSpec:"+heightMeasureSpec); 84 | if (MAX_WIDTH == 0) { 85 | int width = measureWidth(widthMeasureSpec); 86 | if (width > 0) { 87 | MAX_WIDTH = width; 88 | if (imagesList != null && imagesList.size() > 0) { 89 | setList(imagesList); 90 | } 91 | } 92 | } 93 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 94 | } 95 | 96 | /** 97 | * Determines the width of this view 98 | * 99 | * @param measureSpec A measureSpec packed into an int 100 | * @return The width of the view, honoring constraints from measureSpec 101 | */ 102 | private int measureWidth(int measureSpec) { 103 | int result = 0; 104 | int specMode = MeasureSpec.getMode(measureSpec); 105 | int specSize = MeasureSpec.getSize(measureSpec); 106 | 107 | if (specMode == MeasureSpec.EXACTLY) { 108 | // We were told how big to be 109 | result = specSize; 110 | } else { 111 | // Measure the text 112 | // result = (int) mTextPaint.measureText(mText) + getPaddingLeft() 113 | // + getPaddingRight(); 114 | if (specMode == MeasureSpec.AT_MOST) { 115 | // Respect AT_MOST value if that was what is called for by 116 | // measureSpec 117 | result = Math.min(result, specSize); 118 | } 119 | } 120 | return result; 121 | } 122 | 123 | private void initImageLayoutParams() { 124 | int wrap = LayoutParams.WRAP_CONTENT; 125 | int match = LayoutParams.MATCH_PARENT; 126 | 127 | onePicPara = new LayoutParams(wrap, wrap); 128 | 129 | moreParaColumnFirst = new LayoutParams(pxMoreWandH, pxMoreWandH); 130 | morePara = new LayoutParams(pxMoreWandH, pxMoreWandH); 131 | morePara.setMargins(pxImagePadding, 0, 0, 0); 132 | 133 | rowPara = new LayoutParams(match, wrap); 134 | } 135 | 136 | // 根据imageView的数量初始化不同的View布局,还要为每一个View作点击效果 137 | private void initView() { 138 | Log.i("tag00","initView()"); 139 | 140 | this.setOrientation(VERTICAL); 141 | this.removeAllViews(); 142 | if (MAX_WIDTH == 0) { 143 | //为了触发onMeasure()来测量MultiImageView的最大宽度,MultiImageView的宽设置为match_parent 144 | // addView(new View(getContext())); 145 | 146 | measure(1073742808,0); 147 | 148 | return; 149 | } 150 | 151 | if (imagesList == null || imagesList.size() == 0) { 152 | return; 153 | } 154 | 155 | if (imagesList.size() == 1) { 156 | addView(createImageView(0, false)); 157 | } else { 158 | Log.i("tag00","initView else"); 159 | 160 | int allCount = imagesList.size() > 9 ? 9 : imagesList.size();//最多展示九张图片 161 | if (allCount == 4) { 162 | MAX_PER_ROW_COUNT = 2; 163 | } else { 164 | MAX_PER_ROW_COUNT = 3; 165 | } 166 | int rowCount = allCount / MAX_PER_ROW_COUNT + (allCount % MAX_PER_ROW_COUNT > 0 ? 1 : 0);// 行数 167 | for (int rowCursor = 0; rowCursor < rowCount; rowCursor++) { 168 | LinearLayout rowLayout = new LinearLayout(getContext()); 169 | rowLayout.setOrientation(LinearLayout.HORIZONTAL); 170 | 171 | rowLayout.setLayoutParams(rowPara); 172 | if (rowCursor != 0) { 173 | rowLayout.setPadding(0, pxImagePadding, 0, 0); 174 | } 175 | 176 | int columnCount = allCount % MAX_PER_ROW_COUNT == 0 ? MAX_PER_ROW_COUNT : allCount % 177 | MAX_PER_ROW_COUNT;//每行的列数 178 | if (rowCursor != rowCount - 1) { 179 | columnCount = MAX_PER_ROW_COUNT; 180 | } 181 | addView(rowLayout); 182 | 183 | int rowOffset = rowCursor * MAX_PER_ROW_COUNT;// 行偏移 184 | for (int columnCursor = 0; columnCursor < columnCount; columnCursor++) { 185 | int position = columnCursor + rowOffset; 186 | rowLayout.addView(createImageView(position, true)); 187 | } 188 | } 189 | } 190 | } 191 | 192 | private View createImageView(int position, final boolean isMultiImage) { 193 | 194 | String url = imagesList.get(position); 195 | ImageView imageView = new ColorFilterImageView(getContext()); 196 | if (isMultiImage) { 197 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 198 | imageView.setLayoutParams(position % MAX_PER_ROW_COUNT == 0 ? moreParaColumnFirst : morePara); 199 | } else { 200 | imageView.setAdjustViewBounds(true); 201 | // imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 202 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 203 | imageView.setMaxHeight(pxOneMaxWandH); 204 | imageView.setLayoutParams(onePicPara); 205 | } 206 | 207 | imageView.setId(url.hashCode()); 208 | imageView.setOnClickListener(new ImageOnClickListener(position)); 209 | Glide.with(getContext()).load(url).placeholder(R.drawable.default_image).error(R.drawable.default_image) 210 | .diskCacheStrategy(DiskCacheStrategy.ALL).dontAnimate().into(imageView); 211 | 212 | if (position == 8 && imagesList.size() > 9) {//最后一张图片显示还有多少图片未显示 213 | RelativeLayout layout = new RelativeLayout(getContext()); 214 | layout.addView(imageView); 215 | TextView textView = new TextView(getContext()); 216 | textView.setSingleLine(true); 217 | textView.setText("+" + (imagesList.size() - 9)); 218 | textView.setTextSize(DisplayUtils.sp2px(getContext(), 12)); 219 | RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) imageView.getLayoutParams(); 220 | params.addRule(RelativeLayout.CENTER_IN_PARENT); 221 | textView.setBackgroundResource(R.color.colorPrimary); 222 | textView.setTextColor(Color.WHITE); 223 | textView.setGravity(Gravity.CENTER); 224 | layout.addView(textView, params); 225 | return layout; 226 | } 227 | return imageView; 228 | } 229 | 230 | private class ImageOnClickListener implements View.OnClickListener { 231 | 232 | private int position; 233 | 234 | public ImageOnClickListener(int position) { 235 | this.position = position; 236 | } 237 | 238 | @Override 239 | public void onClick(View view) { 240 | if (mOnItemClickListener != null) { 241 | mOnItemClickListener.onItemClick(view, position); 242 | } 243 | } 244 | } 245 | 246 | public interface OnItemClickListener { 247 | public void onItemClick(View view, int position); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/recyclerview/BaseRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test.recyclerview; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by xiaohu on 2016/9/20. 14 | */ 15 | public abstract class BaseRecyclerAdapter extends RecyclerView.Adapter { 16 | 17 | protected final List mData; 18 | protected final Context mContext; 19 | protected LayoutInflater mInflater; 20 | private OnItemClickListener mClickListener; 21 | private OnItemLongClickListener mLongClickListener; 22 | 23 | public BaseRecyclerAdapter(Context ctx, List list) { 24 | mData = (list != null) ? list : new ArrayList(); 25 | mContext = ctx; 26 | mInflater = LayoutInflater.from(ctx); 27 | } 28 | 29 | 30 | @Override 31 | public BaseRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 32 | final BaseRecyclerViewHolder holder = new BaseRecyclerViewHolder(mContext, mInflater.inflate(getItemLayoutId(viewType), parent, false)); 33 | if (mClickListener != null) { 34 | holder.itemView.setOnClickListener(new View.OnClickListener() { 35 | @Override 36 | public void onClick(View v) { 37 | mClickListener.onItemClick(holder.itemView, holder.getLayoutPosition()); 38 | } 39 | }); 40 | } 41 | if (mLongClickListener != null) { 42 | holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { 43 | @Override 44 | public boolean onLongClick(View v) { 45 | mLongClickListener.onItemLongClick(holder.itemView, holder.getLayoutPosition()); 46 | return true; 47 | } 48 | }); 49 | } 50 | return holder; 51 | } 52 | 53 | @Override 54 | public void onBindViewHolder(BaseRecyclerViewHolder holder, int position) { 55 | bindData(holder, position, mData.get(position)); 56 | } 57 | 58 | 59 | @Override 60 | public int getItemCount() { 61 | return mData.size(); 62 | } 63 | 64 | public void add(int pos, T item) { 65 | mData.add(pos, item); 66 | notifyItemInserted(pos); 67 | } 68 | 69 | public void delete(int pos) { 70 | mData.remove(pos); 71 | notifyItemRemoved(pos); 72 | } 73 | 74 | 75 | public void setOnItemClickListener(OnItemClickListener listener) { 76 | mClickListener = listener; 77 | } 78 | 79 | public void setOnItemLongClickListener(OnItemLongClickListener listener) { 80 | mLongClickListener = listener; 81 | } 82 | 83 | abstract public int getItemLayoutId(int viewType); 84 | 85 | abstract public void bindData(BaseRecyclerViewHolder holder, int position, T item); 86 | 87 | public interface OnItemClickListener { 88 | public void onItemClick(View itemView, int pos); 89 | } 90 | 91 | public interface OnItemLongClickListener { 92 | public void onItemLongClick(View itemView, int pos); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/recyclerview/BaseRecyclerViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test.recyclerview; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.util.SparseArray; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.EditText; 9 | import android.widget.ImageButton; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | import android.widget.TextView; 13 | 14 | /** 15 | * Created by xiaohu on 2016/9/20. 16 | */ 17 | public class BaseRecyclerViewHolder extends RecyclerView.ViewHolder { 18 | 19 | private SparseArray mViews;//集合类,layout里包含的View,以view的id作为key,value是view对象 20 | private Context mContext;//上下文对象 21 | 22 | public BaseRecyclerViewHolder(Context ctx, View itemView) { 23 | super(itemView); 24 | mContext = ctx; 25 | mViews = new SparseArray(); 26 | } 27 | 28 | private T findViewById(int viewId) { 29 | View view = mViews.get(viewId); 30 | if (view == null) { 31 | view = itemView.findViewById(viewId); 32 | mViews.put(viewId, view); 33 | } 34 | return (T) view; 35 | } 36 | 37 | 38 | public View getView(int viewId) { 39 | return findViewById(viewId); 40 | } 41 | 42 | public TextView getTextView(int viewId) { 43 | return (TextView) getView(viewId); 44 | } 45 | 46 | public Button getButton(int viewId) { 47 | return (Button) getView(viewId); 48 | } 49 | 50 | public ImageView getImageView(int viewId) { 51 | return (ImageView) getView(viewId); 52 | } 53 | 54 | public LinearLayout getLinearLayout(int viewId){return (LinearLayout) getView(viewId);} 55 | 56 | 57 | public ImageButton getImageButton(int viewId) { 58 | return (ImageButton) getView(viewId); 59 | } 60 | 61 | public EditText getEditText(int viewId) { 62 | return (EditText) getView(viewId); 63 | } 64 | 65 | public BaseRecyclerViewHolder setText(int viewId, String value) { 66 | TextView view = findViewById(viewId); 67 | view.setText(value); 68 | return this; 69 | } 70 | 71 | public BaseRecyclerViewHolder setBackground(int viewId, int resId) { 72 | View view = findViewById(viewId); 73 | view.setBackgroundResource(resId); 74 | return this; 75 | } 76 | 77 | public BaseRecyclerViewHolder setClickListener(int viewId, View.OnClickListener listener) { 78 | View view = findViewById(viewId); 79 | view.setOnClickListener(listener); 80 | return this; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/recyclerview/ChatRoomMsgBean.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test.recyclerview; 2 | 3 | /** 4 | * Created by Administrator on 2016/11/28. 5 | */ 6 | 7 | public class ChatRoomMsgBean { 8 | private String name; 9 | private String content; 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | public void setName(String name) { 16 | this.name = name; 17 | } 18 | 19 | public String getContent() { 20 | return content; 21 | } 22 | 23 | public void setContent(String content) { 24 | this.content = content; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/administrator/test/recyclerview/CustomScrollView.java: -------------------------------------------------------------------------------- 1 | package com.example.administrator.test.recyclerview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ScrollView; 6 | 7 | /** 8 | * Created by Administrator on 2016/11/28. 9 | */ 10 | 11 | public class CustomScrollView extends ScrollView { 12 | public CustomScrollView(Context context) { 13 | super(context); 14 | } 15 | 16 | public CustomScrollView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | } 23 | @Override 24 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 25 | int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, 26 | MeasureSpec.AT_MOST); 27 | super.onMeasure(widthMeasureSpec, expandSpec); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/default_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soon-gz/MultipleImageView/cec48230dff39224c3d9bc5295a9a420bf614c06/app/src/main/res/drawable/default_image.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 17 | 21 | 26 | 27 | 32 | 33 | 34 | 35 |