Android之仿微信图片选择器

  先上效果图。第一张图显示的是“相机”文件夹中的所有图片;通过点击多张图片可以到第二张图所示的效果(被选择的图片会变暗,同时选择按钮变亮);点击最下面的那一栏可以到第三张图所示的效果(显示手机中所有包含图片文件的文件夹)。

   

 

一、目标

、尽可能避免内存溢出A、根据图片的显示大小去压缩图片B、使用缓存对图片进行管理(LruCache)
、保证用户操作UI尽可能的流畅在getView()方法中尽量不做耗时操作(异步显示 + 回调显示)
、用户预期显示的图片必须尽可能快的显示(图片加载策略的选择:LIFO后进先出 / FIFO先进先出),我们采用采用LIFO(后进先出的策略)

 

二、思路

1、图片的加载在Adapter的getView()方法中执行,我们根据一个图片的URL到LruCache缓存中寻找Bitmap图片,如果找到则返回图片,如果找不到,则会根据URL产生一个Task并放到TaskQueue中,同时发送一个通知提醒后台轮询线程,轮询线程从TaskQueue中取出一个Task到线程池去执行(执行的是Task的run()方法,具体为:获得图片显示的实际大小;使用Options对图片进行压缩;加载图片且放入LruCache)。我们需要在ImageLoader类中用到:LruCache缓存、线程池、任务列表TaskQueue、后台轮询线程、与轮询线程绑定的Handler和UI线程的Handler

2、具体的实现:Handler + Looper + Message(Android异步消息处理框架)。当我们用Handler发送消息时,会把信息放到MessageQueue中,轮询线程会取出这条消息,交给Handler的handleMessage()方法进行处理

3、后台轮询线程(Thread)不断访问任务队列(LinkList<Runnable>),如果任务队列中有加载图片的任务 (Runnable),就通过Handler发消息给线程池(ExecuterService),让线程池拿出一个子线程,然后根据调度任务的策略 (LIFO)从任务队列中取出一个任务去完成图片的获取,因为图片是异步的在子线程中获取到的,不能直接显示,所以需要通过一个UI相关的Handler把图片对象发送到UI线程中,最后完成图片的显示。

 

三、代码和解释

(一)目录结构图如下:

(二)代码和解释(解释都在代码的注释中):

MainActivity的布局文件activity_main.xml中的代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="fill_parent"
 4     android:layout_height="fill_parent"
 5     android:background="@color/cl_white">
 6 
 7     <RelativeLayout
 8         android:layout_width="match_parent"
 9         android:layout_height="50.0dip"
10         android:background="#ee000000">
11 
12         <ImageView
13             android:id="@+id/position_main_iv_icon"
14             android:layout_width="35.0dip"
15             android:layout_height="35.0dip"
16             android:layout_centerVertical="true"
17             android:layout_marginLeft="10.0dip"
18             android:src="@mipmap/ic_launcher" />
19 
20         <TextView
21             style="@style/Style_Main_TextView"
22             android:layout_marginLeft="10.0dip"
23             android:layout_toRightOf="@+id/position_main_iv_icon"
24             android:text="图片选择器" />
25     </RelativeLayout>
26 
27     <RelativeLayout
28         android:id="@+id/find_main_rl_bottomlayout"
29         android:layout_width="match_parent"
30         android:layout_height="50.0dip"
31         android:layout_alignParentBottom="true"
32         android:background="#ee000000">
33 
34         <TextView
35             android:id="@+id/find_main_tv_toall"
36             style="@style/Style_Main_TextView"
37             android:layout_marginLeft="10.0dip"
38             android:text="所有图片" />
39 
40         <TextView
41             android:id="@+id/find_main_tv_num"
42             style="@style/Style_Main_TextView"
43             android:layout_alignParentRight="true"
44             android:layout_marginRight="10.0dip"
45             android:text="共100张" />
46     </RelativeLayout>
47 
48     <GridView
49         android:id="@+id/find_main_gv_images"
50         android:layout_width="fill_parent"
51         android:layout_height="fill_parent"
52         android:layout_marginBottom="50.0dip"
53         android:layout_marginTop="50.0dip"
54         android:cacheColorHint="@android:color/transparent"
55         android:horizontalSpacing="3.0dip"
56         android:listSelector="@android:color/transparent"
57         android:numColumns="3"
58         android:stretchMode="columnWidth"
59         android:verticalSpacing="3.0dip" />
60 
61 </RelativeLayout>

MainActivity.java中的代码:

  1 package com.itgungnir.activity;
  2 
  3 import android.app.Activity;
  4 import android.app.ProgressDialog;
  5 import android.content.ContentResolver;
  6 import android.database.Cursor;
  7 import android.net.Uri;
  8 import android.os.Bundle;
  9 import android.os.Environment;
 10 import android.os.Handler;
 11 import android.os.Message;
 12 import android.provider.MediaStore;
 13 import android.view.View;
 14 import android.view.WindowManager;
 15 import android.widget.GridView;
 16 import android.widget.PopupWindow;
 17 import android.widget.RelativeLayout;
 18 import android.widget.TextView;
 19 import android.widget.Toast;
 20 
 21 import com.itgungnir.entity.FolderModel;
 22 import com.itgungnir.tools.ImageAdapter;
 23 import com.itgungnir.view.DirsPopWindow;
 24 
 25 import java.io.File;
 26 import java.io.FilenameFilter;
 27 import java.util.ArrayList;
 28 import java.util.Arrays;
 29 import java.util.HashSet;
 30 import java.util.List;
 31 import java.util.Set;
 32 
 33 public class MainActivity extends Activity {
 34     private GridView imageGrid;
 35     private TextView dirName;
 36     private TextView dirCount;
 37     private ProgressDialog progressDialog; // 加载图片时出现的加载对话框
 38     private DirsPopWindow popWindow; // 可弹出的目录菜单
 39     private RelativeLayout bottomLayout;
 40 
 41     private List<String> imageList; // 图片的数据集
 42     private File currentDir; // 当前所在的文件目录
 43     private int totalCount; // 显示dirCount中的数据
 44     private List<FolderModel> folderModels = new ArrayList<FolderModel>();
 45     private ImageAdapter imageAdapter;
 46 
 47     private static final int DATA_LOADED = 0x110;
 48 
 49     private Handler handler = new Handler() {
 50         @Override
 51         public void handleMessage(Message msg) {
 52             if (msg.what == DATA_LOADED) {
 53                 progressDialog.dismiss();
 54                 bindDataToView();
 55 
 56                 initDirsPopWindow();
 57             }
 58         }
 59     };
 60 
 61     @Override
 62     protected void onCreate(Bundle savedInstanceState) {
 63         super.onCreate(savedInstanceState);
 64         setContentView(R.layout.activity_main);
 65         initView();
 66         initData();
 67         initEvent();
 68     }
 69 
 70     /**
 71      * 初始化弹出菜单
 72      */
 73     private void initDirsPopWindow() {
 74         popWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
 75             @Override
 76             public void onDismiss() {
 77                 lightOn();
 78             }
 79         });
 80     }
 81 
 82     /**
 83      * 弹出窗口消失之后,需要将后面的图片列表变亮
 84      */
 85     private void lightOn() {
 86         WindowManager.LayoutParams lp = getWindow().getAttributes();
 87         lp.alpha = 1.0f;
 88         getWindow().setAttributes(lp);
 89     }
 90 
 91     /**
 92      * 在现实PopUpWindow之后将后面的图片列表置黑
 93      */
 94     private void lightOff() {
 95         WindowManager.LayoutParams lp = getWindow().getAttributes();
 96         lp.alpha = 0.3f;
 97         getWindow().setAttributes(lp);
 98     }
 99 
100     /**
101      * 绑定数据到View中
102      */
103     private void bindDataToView() {
104         if (currentDir == null) {
105             Toast.makeText(MainActivity.this, "未扫描到任何图片!", Toast.LENGTH_SHORT).show();
106             return;
107         }
108         imageList = Arrays.asList(currentDir.list());
109         imageAdapter = new ImageAdapter(MainActivity.this, imageList, currentDir.getAbsolutePath());
110         imageGrid.setAdapter(imageAdapter);
111 
112         dirCount.setText(totalCount + "");
113         dirName.setText(currentDir.getName());
114     }
115 
116     /**
117      * 初始化数据(利用ContentProvider扫描手机中的所有图片)
118      */
119     private void initData() {
120         if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
121             Toast.makeText(MainActivity.this, "当前存储卡不可用!", Toast.LENGTH_SHORT).show();
122             return;
123         }
124         progressDialog = ProgressDialog.show(MainActivity.this, null, "正在加载...");
125         new Thread() {
126             @Override
127             public void run() {
128                 /**
129                  * 初始化FolderModel,为PopUpWindow做准备
130                  */
131                 Uri imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
132                 ContentResolver cr = MainActivity.this.getContentResolver();
133                 Cursor cursor = cr.query(imageUri, null, MediaStore.Images.Media.MIME_TYPE + " = ? or " + MediaStore.Images.Media.MIME_TYPE + " = ? ",
134                         new String[]{"image/jpeg", "image/png"}, MediaStore.Images.Media.DATE_MODIFIED);
135                 Set<String> dirPaths = new HashSet<String>();
136                 while (cursor.moveToNext()) {
137                     String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); // 获取图片的路径
138                     File parentFile = new File(path).getParentFile(); // 获取该图片所在的父路径名
139                     if (parentFile == null) {
140                         continue;
141                     }
142                     String dirPath = parentFile.getAbsolutePath();
143                     FolderModel folder = null;
144                     // 放置重复扫描
145                     if (dirPaths.contains(dirPath)) {
146                         continue;
147                     } else {
148                         dirPaths.add(dirPath);
149                         folder = new FolderModel();
150                         folder.setDir(dirPath);
151                         folder.setFirstImgPath(path);
152                     }
153                     if (parentFile.list() == null) {
154                         continue;
155                     }
156                     int picSize = parentFile.list(new FilenameFilter() {
157                         @Override
158                         public boolean accept(File dir, String filename) {
159                             if (filename.endsWith(".jpg") || filename.endsWith(".jpeg") || filename.endsWith(".png")) {
160                                 return true;
161                             }
162                             return false;
163                         }
164                     }).length;
165                     folder.setCount(picSize);
166                     folderModels.add(folder);
167                     if (picSize > totalCount) {
168                         totalCount = picSize;
169                         currentDir = parentFile;
170                     }
171                 }
172                 cursor.close();
173                 // 扫描完成,释放临时变量的内存
174                 dirPaths = null;
175                 handler.sendEmptyMessage(DATA_LOADED); // 通知Handler扫描图片完成
176             }
177         }.start();
178     }
179 
180     /**
181      * 初始化事件
182      */
183     private void initEvent() {
184         bottomLayout.setOnClickListener(new View.OnClickListener() {
185             @Override
186             public void onClick(View v) {
187                 popWindow.setAnimationStyle(R.style.Style_PopWindow_Anim);
188                 popWindow.showAsDropDown(bottomLayout, 0, 0);
189                 lightOff();
190             }
191         });
192 
193         popWindow.setOnDirSelectListener(new DirsPopWindow.OnDirSelectListener() {
194             @Override
195             public void onDirSelected(FolderModel folder) {
196                 currentDir = new File(folder.getDir());
197                 imageList = Arrays.asList(currentDir.list(new FilenameFilter() {
198                     @Override
199                     public boolean accept(File dir, String filename) {
200                         if (filename.endsWith(".jpg") || filename.endsWith(".jpeg") || filename.endsWith(".png")) {
201                             return true;
202                         }
203                         return false;
204                     }
205                 }));
206                 imageAdapter = new ImageAdapter(MainActivity.this, imageList, currentDir.getAbsolutePath());
207                 imageGrid.setAdapter(imageAdapter);
208                 dirCount.setText(imageList.size() + "");
209                 dirName.setText(folder.getName());
210 
211                 popWindow.dismiss();
212             }
213         });
214     }
215 
216     /**
217      * 初始化控件
218      */
219     private void initView() {
220         imageGrid = (GridView) findViewById(R.id.find_main_gv_images);
221         dirName = (TextView) findViewById(R.id.find_main_tv_toall);
222         dirCount = (TextView) findViewById(R.id.find_main_tv_num);
223         bottomLayout = (RelativeLayout) findViewById(R.id.find_main_rl_bottomlayout);
224         popWindow = new DirsPopWindow(MainActivity.this, folderModels);
225     }
226 
227     @Override
228     protected void onDestroy() {
229         progressDialog.dismiss();
230         super.onDestroy();
231     }
232 }

图片压缩加载类ImageLoader.java中的代码:

  1 package com.itgungnir.tools;
  2 
  3 import java.lang.reflect.Field;
  4 import java.util.LinkedList;
  5 import java.util.concurrent.ExecutorService;
  6 import java.util.concurrent.Executors;
  7 import java.util.concurrent.Semaphore;
  8 
  9 import android.graphics.Bitmap;
 10 import android.graphics.BitmapFactory;
 11 import android.os.Handler;
 12 import android.os.Looper;
 13 import android.os.Message;
 14 import android.support.v4.util.LruCache;
 15 import android.util.DisplayMetrics;
 16 import android.util.Log;
 17 import android.view.ViewGroup.LayoutParams;
 18 import android.widget.ImageView;
 19 
 20 /**
 21  * ********图片加载类*********
 22  * ***思路:图片的加载在Adapter的getView()方法中执行,我们根据一个图片的URL到LruCache缓存中寻找Bitmap图片,如果找到则返回图片,
 23  * 如果找不到,则会根据URL产生一个Task并放到TaskQueue中,同时发送一个通知提醒后台轮询线程,轮询线程从TaskQueue中取出一个Task到线程池去执行(执行的是Task的run()方法,
 24  * 具体为:获得图片显示的实际大小;使用Options对图片进行压缩;加载图片且放入LruCache)
 25  * ***核心:Handler + Looper + Message(Android异步消息处理框架)
 26  * 当我们用Handler发送消息时,会把信息放到MessageQueue中,轮询线程会取出这条消息,交给Handler的handleMessage()方法进行处理
 27  */
 28 public class ImageLoader {
 29     private static ImageLoader mInstance; // 实例
 30     private LruCache<String, Bitmap> mLruCache; // 图片缓存的核心类
 31     private ExecutorService mThreadPool; // 线程池
 32     private static final int DEFAULT_THREAD_COUNT = 1; // 线程池的线程数量,默认为1
 33     private Type mType = Type.LIFO; // 任务队列的默认调度方式
 34     private LinkedList<Runnable> taskQueue; // 任务队列
 35     private Thread mPoolThread; // 后台轮询线程
 36     private Handler mPoolThreadHandler; // 与后台轮询线程绑定的Handler
 37     private Handler uiHandler; // 运行在UI线程的handler,用于给ImageView设置图片
 38     /**
 39      * 引入一个值为1的信号量,防止mPoolThreadHander未初始化完成
 40      * Semaphore的作用是限制某一资源的同步访问
 41      * 可以把Semaphore理解成一个可以容纳N个人的房间,如果人数没有达到N就可以进去,如果人满了,就要等待有人出来才可以进去
 42      * 在addTask()方法中需要用到后台轮询线程poolThreadHandler,但存在线程同步问题,
 43      * 即addTask()方法可能在poolThreadHandler初始化之前就被调用了,所以我们需要定义这样一个“信号量”来调控线程同步
 44      */
 45     private volatile Semaphore mPoolThreadHandlerSemaphore = new Semaphore(0); // 控制addTask()方法在mPoolThreadHandler吃实话之后才能调用
 46     private volatile Semaphore mPoolSemaphore; // 引入一个值为1的信号量,由于线程池内部也有一个阻塞线程,防止加入任务的速度过快,使LIFO效果不明显
 47 
 48     /**
 49      * 图片加载的策略(FIFO先进先出、LIFO后进先出)
 50      */
 51     public enum Type {
 52         FIFO, LIFO
 53     }
 54 
 55     /**
 56      * 构造函数
 57      * 由于ImageLoader中需要使用LruCache来缓存图片,需要占据较大的空间,所以整个项目中只需要一个ImageLoader即可(需要使用单例模式)
 58      * 因此我们把构造方法设为private权限,防止用户new出实例
 59      *
 60      * @param threadCount 任务队列中默认线程数
 61      * @param type        图片加载策略(先进先出/后进先出)
 62      */
 63     private ImageLoader(int threadCount, Type type) {
 64         init(threadCount, type);
 65     }
 66 
 67     /**
 68      * 单例获得该实例对象(无参数,按照默认值进行初始化)
 69      */
 70     public static ImageLoader getInstance() {
 71         if (mInstance == null) {
 72             synchronized (ImageLoader.class) {
 73                 if (mInstance == null) {
 74                     mInstance = new ImageLoader(DEFAULT_THREAD_COUNT, Type.LIFO);
 75                 }
 76             }
 77         }
 78         return mInstance;
 79     }
 80 
 81     /**
 82      * 单例获得该实例对象(有参数,用户可以根据实际需要对ImageLoader进行实例化)
 83      */
 84     public static ImageLoader getInstance(int threadCount, Type type) {
 85         if (mInstance == null) {
 86             synchronized (ImageLoader.class) {
 87                 if (mInstance == null) {
 88                     mInstance = new ImageLoader(threadCount, type);
 89                 }
 90             }
 91         }
 92         return mInstance;
 93     }
 94 
 95     /**
 96      * 完成成员变量的初始化操作
 97      */
 98     private void init(int threadCount, Type type) {
 99         /**
100          * 初始化后台轮询线程(使用Looper、Handler、Message实现)
101          */
102         mPoolThread = new Thread() {
103             @Override
104             public void run() {
105                 Looper.prepare();
106                 mPoolThreadHandler = new Handler() {
107                     @Override
108                     public void handleMessage(Message msg) {
109                         mThreadPool.execute(getTask()); // 线程池取出一个任务去执行
110                         try {
111                             mPoolSemaphore.acquire();
112                         } catch (InterruptedException e) {
113                         }
114                     }
115                 };
116                 mPoolThreadHandlerSemaphore.release(); // 释放一个信号量
117                 Looper.loop(); // 开始无限循环
118             }
119         };
120         mPoolThread.start();
121         /**
122          * 初始化LruCache
123          */
124         int maxMemory = (int) Runtime.getRuntime().maxMemory(); // 获取应用程序最大可用内存
125         int cacheSize = maxMemory / 8; // 设置缓存的存储空间是总空间的1/8
126         mLruCache = new LruCache<String, Bitmap>(cacheSize) {
127             @Override
128             protected int sizeOf(String key, Bitmap value) {
129                 return value.getRowBytes() * value.getHeight(); // getRowBytes()获取到图片每行有多少字节,乘以图片的高度就是图片占据的内存
130             }
131         };
132         /**
133          * 初始化其他
134          */
135         mThreadPool = Executors.newFixedThreadPool(threadCount); // 初始化线程池threadPool
136         mPoolSemaphore = new Semaphore(threadCount); // 初始化线程池(消息队列)信号量
137         taskQueue = new LinkedList<Runnable>(); // 初始化任务队列taskQueue
138         mType = type == null ? Type.LIFO : type; // 同步Type
139     }
140 
141     /**
142      * 根据路径path找到对应的图片并异步加载到主界面中
143      *
144      * @param path      图片的路径
145      * @param imageView 目标的ImageView(图片将要被加载到的ImageView)
146      */
147     public void loadImage(final String path, final ImageView imageView) {
148         imageView.setTag(path); // 将PATH设置为imageView的TAG,方便在主线程中对比、设置图片
149         // UI线程
150         if (uiHandler == null) {
151             uiHandler = new Handler() {
152                 @Override
153                 public void handleMessage(Message msg) {
154                     ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
155                     ImageView imageView = holder.imageView;
156                     Bitmap bm = holder.bitmap;
157                     String path = holder.path;
158                     // 将path与imageView的tag进行比较,如果相同,则为imageView设置图片
159                     if (imageView.getTag().toString().equals(path)) {
160                         imageView.setImageBitmap(bm);
161                     }
162                 }
163             };
164         }
165 
166         Bitmap bm = getBitmapFromLruCache(path); // 根据路径Path在缓存中获取Bitmap
167         if (bm != null) { // 如果这张图片存在于缓存中,则通知UI线程更新图片
168             ImgBeanHolder holder = new ImgBeanHolder();
169             holder.bitmap = bm;
170             holder.imageView = imageView;
171             holder.path = path;
172             Message message = Message.obtain();
173             message.obj = holder;
174             uiHandler.sendMessage(message);
175         } else { // 如果这张图片没有存在于缓存中,则添加一个新的任务到任务队列中
176             addTask(new Runnable() { // 添加一个任务到任务队列中
177                 @Override
178                 public void run() {
179                     /**
180                      * 加载图片
181                      */
182                     // 压缩图片:1、获取图片需要显示的大小
183                     ImageSize imageSize = getImageViewSize(imageView);
184                     // 压缩图片:2、压缩图片
185                     int reqWidth = imageSize.width;
186                     int reqHeight = imageSize.height;
187                     Bitmap bm = decodeSampledBitmapFromResource(path, reqWidth, reqHeight);
188                     // 压缩图片:3、将图片加入到缓存
189                     addBitmapToLruCache(path, bm);
190                     // 将上面操作获取到的数据封装到ImgBeanHolder实体对象中,通知UI线程处理
191                     ImgBeanHolder holder = new ImgBeanHolder();
192                     holder.bitmap = getBitmapFromLruCache(path);
193                     holder.imageView = imageView;
194                     holder.path = path;
195                     Message message = Message.obtain();
196                     message.obj = holder;
197                     uiHandler.sendMessage(message);
198                     mPoolSemaphore.release(); // 为线程池释放一个信号量
199                 }
200             });
201         }
202     }
203 
204     /**
205      * 添加一个任务到任务队列
206      * synchronized是为了避免多个线程同时到达这个方法中申请信号量而发生死锁
207      */
208     private synchronized void addTask(Runnable runnable) {
209         /**
210          * 请求资源信号量,避免死锁
211          * 如果还没有对mPoolThreadHandler进行初始化,则默认的房间里可以容纳0个人,所以如果此时addTask()方法请求资源会被阻塞
212          * 通过这个信号量控制addTask()方法必须在mPoolThreadHandler初始化之后才调用
213          */
214         try {
215             if (mPoolThreadHandler == null)
216                 mPoolThreadHandlerSemaphore.acquire(); // 如果当前mPoolThreadHandler还没有初始化,则该线程阻塞(等待),直到mPoolThreadHandler初始化
217         } catch (InterruptedException e) {
218             e.printStackTrace();
219         }
220         taskQueue.add(runnable); // 添加Runnable任务到任务队列
221         mPoolThreadHandler.sendEmptyMessage(0x110); // 发送一个通知,通知后台轮询线程,0x110是一个随意的值
222     }
223 
224     /**
225      * 从任务队列中取出一个任务
226      * 根据为ImageLoader实例设定的图片加载策略决定是取出最后一个还是取出第一个
227      */
228     private synchronized Runnable getTask() {
229         if (mType == Type.FIFO) {
230             return taskQueue.removeFirst();
231         } else if (mType == Type.LIFO) {
232             return taskQueue.removeLast();
233         }
234         return null;
235     }
236 
237     /**
238      * 根据ImageView获得适当的压缩的目标宽和高
239      */
240     private ImageSize getImageViewSize(ImageView imageView) {
241         /**
242          * 获取ImageView的LayoutParams
243          */
244         final DisplayMetrics metrics = imageView.getContext().getResources().getDisplayMetrics();
245         final LayoutParams lp = imageView.getLayoutParams();
246         /**
247          * 定义ImageView显示的宽度
248          */
249         int width = lp.width == LayoutParams.WRAP_CONTENT ? 0 : imageView.getWidth(); // 获取ImageView的实际宽度
250         if (width <= 0) // ImageView可能是刚new出来就来执行这个方法,所以还没有宽高值,只能通过在layout中声明的宽高值来赋值
251             width = lp.width; // 获取ImageView在layout中声明的宽度
252         if (width <= 0) // 在layout中设置的宽高值是WRAP_CONTENT或MATCH_PARENT,则还是取出0,这时我们就需要看ImageView有没有设置max值
253             width = getImageViewFieldValue(imageView, "mMaxWidth"); // 通过反射获取ImageView的宽度最大值
254         if (width <= 0) // 可能ImageView没有设置max值,因此我们只能设置ImageView的宽或高为屏幕的宽或高
255             width = metrics.widthPixels;
256         /**
257          * 定义ImageView显示的高度(同宽度)
258          */
259         int height = lp.height == LayoutParams.WRAP_CONTENT ? 0 : imageView.getHeight();
260         if (height <= 0) height = lp.height;
261         if (height <= 0) height = getImageViewFieldValue(imageView, "mMaxHeight");
262         if (height <= 0) height = metrics.heightPixels;
263         /**
264          * 将ImageView压缩后的宽和高封装到ImageSize实体类中返回
265          */
266         ImageSize imageSize = new ImageSize();
267         imageSize.width = width;
268         imageSize.height = height;
269         return imageSize;
270     }
271 
272     /**
273      * 从LruCache中获取一张图片,如果不存在就返回null
274      */
275     private Bitmap getBitmapFromLruCache(String key) {
276         return mLruCache.get(key);
277     }
278 
279     /**
280      * 将图片添加到LruCache缓存
281      *
282      * @param path   路径,先要判断图片是否已经在缓存中
283      * @param bitmap 图片
284      */
285     private void addBitmapToLruCache(String path, Bitmap bitmap) {
286         if (getBitmapFromLruCache(path) == null) {
287             if (bitmap != null)
288                 mLruCache.put(path, bitmap);
289         }
290     }
291 
292     /**
293      * 根据图片需求的宽高和图片实际的宽高计算inSampleSize(图片压缩的比例,用于压缩图片)
294      *
295      * @param options   图片实际的宽和高
296      * @param reqWidth  图片需要的宽度
297      * @param reqHeight 图片需要的高度
298      */
299     private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
300         int width = options.outWidth; // 原图片的宽度
301         int height = options.outHeight; // 原图片的高度
302         int inSampleSize = 1; // 缩放的比例
303         if (width > reqWidth && height > reqHeight) {
304             int widthRatio = Math.round((float) width / (float) reqWidth); // 计算出实际宽度和目标宽度的比率
305             int heightRatio = Math.round((float) width / (float) reqWidth);
306             inSampleSize = Math.max(widthRatio, heightRatio); // 取宽度缩放值和高度缩放值的较大值作为图片缩放比例
307         }
308         return inSampleSize;
309     }
310 
311     /**
312      * 根据图片需要显示的宽和高对图片进行压缩
313      *
314      * @param path      图片的路径
315      * @param reqWidth  图片显示的宽度
316      * @param reqHeight 图片显示的高度
317      */
318     private Bitmap decodeSampledBitmapFromResource(String path, int reqWidth, int reqHeight) {
319         final BitmapFactory.Options options = new BitmapFactory.Options();
320         options.inJustDecodeBounds = true; // 如果该值设为true那么将不返回实际的bitmap,也不给其分配内存空间(避免内存溢出),但允许我们查询图片信息(包括图片大小信息)
321         BitmapFactory.decodeFile(path, options); // 经过这行代码,options就获得了图片实际的宽和高
322         options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 调用上面定义的方法计算inSampleSize值(图片压缩的比例)
323         options.inJustDecodeBounds = false; // 设置可以取出图片
324         Bitmap bitmap = BitmapFactory.decodeFile(path, options); // 使用获取到的inSampleSize值再次解析图片,取出Bitmap
325         return bitmap;
326     }
327 
328     /**
329      * 存放图片的Bitmap源、ImageView和图片路径的实体类
330      */
331     private class ImgBeanHolder {
332         Bitmap bitmap;
333         ImageView imageView;
334         String path;
335     }
336 
337     /**
338      * 存放ImageView显示的宽高的实体类
339      */
340     private class ImageSize {
341         int width; // ImageView显示的宽度
342         int height; // ImageView显示的高度
343     }
344 
345     /**
346      * 通过反射获取某个Object的fieldName属性对应的值(本程序中是通过反射获得ImageView设置的最大宽度和高度)
347      */
348     private static int getImageViewFieldValue(Object object, String fieldName) {
349         int value = 0;
350         try {
351             Field field = ImageView.class.getDeclaredField(fieldName);
352             field.setAccessible(true);
353             int fieldValue = (Integer) field.get(object);
354             if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
355                 value = fieldValue;
356             }
357         } catch (Exception e) {
358             e.printStackTrace();
359         }
360         return value;
361     }
362 }

图片文件夹实体类FolderModel.java中的代码:

 1 package com.itgungnir.entity;
 2 
 3 /**
 4  * PopUpWindow对应的Bean类
 5  */
 6 public class FolderModel {
 7     private String dir; // 图片的文件夹的路径
 8     private String firstImgPath; // 第一张图片的路径
 9     private String name; // 文件夹的名称
10     private int count; // 文件夹中图片的数量
11 
12     public String getFirstImgPath() {
13         return firstImgPath;
14     }
15 
16     public void setFirstImgPath(String firstImgPath) {
17         this.firstImgPath = firstImgPath;
18     }
19 
20     public String getName() {
21         return name;
22     }
23 
24     public int getCount() {
25         return count;
26     }
27 
28     public void setCount(int count) {
29         this.count = count;
30     }
31 
32     public String getDir() {
33         return dir;
34     }
35 
36     public void setDir(String dir) {
37         this.dir = dir;
38         int lastIndex = this.dir.lastIndexOf("/");
39         this.name = this.dir.substring(lastIndex);
40     }
41 }

主界面GridView的适配器类ImageAdapter.java中的代码:

 1 package com.itgungnir.tools;
 2 
 3 import android.content.Context;
 4 import android.graphics.Color;
 5 import android.view.LayoutInflater;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 import android.widget.BaseAdapter;
 9 import android.widget.ImageButton;
10 import android.widget.ImageView;
11 
12 import com.itgungnir.activity.R;
13 
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17 
18 /**
19  * 主界面MainActivity中的GridView的适配器
20  */
21 public class ImageAdapter extends BaseAdapter {
22     private static final Set<String> selectedImages = new HashSet<String>(); // 存储选中的图片的路径
23 
24     private String dirPath; // GridView中图片的父路径
25     private List<String> imagePaths; // GridView中显示的图片的路径(s)
26     private LayoutInflater inflater;
27 
28     public ImageAdapter(Context context, List<String> data, String dirPath) {
29         this.dirPath = dirPath;
30         this.imagePaths = data;
31         inflater = LayoutInflater.from(context);
32     }
33 
34     @Override
35     public int getCount() {
36         return imagePaths.size();
37     }
38 
39     @Override
40     public Object getItem(int position) {
41         return imagePaths.get(position);
42     }
43 
44     @Override
45     public long getItemId(int position) {
46         return position;
47     }
48 
49     @Override
50     public View getView(final int position, View convertView, ViewGroup parent) {
51         final ViewHolder viewHolder;
52         if (convertView == null) {
53             convertView = inflater.inflate(R.layout.sideworks_griditem, parent, false);
54             viewHolder = new ViewHolder();
55             viewHolder.imageView = (ImageView) convertView.findViewById(R.id.find_griditem_iv_image);
56             viewHolder.checkBtn = (ImageButton) convertView.findViewById(R.id.find_griditem_ib_check);
57             convertView.setTag(viewHolder);
58         } else {
59             viewHolder = (ViewHolder) convertView.getTag();
60         }
61         // 重置状态
62         viewHolder.imageView.setImageResource(R.mipmap.img_pictures_notfound);
63         viewHolder.checkBtn.setImageResource(R.mipmap.img_picture_unselected);
64         viewHolder.imageView.setColorFilter(null);
65         // 加载图片
66         ImageLoader.getInstance(3, ImageLoader.Type.LIFO).loadImage(dirPath + "/" + imagePaths.get(position), viewHolder.imageView);
67 
68         /**
69          * 为GridView的Item设置点击事件
70          */
71         viewHolder.imageView.setOnClickListener(new View.OnClickListener() {
72             @Override
73             public void onClick(View v) {
74                 String imagePath = dirPath + "/" + imagePaths.get(position);
75                 if (selectedImages.contains(imagePath)) { // 如果图片已经被选择,则清楚选择状态
76                     selectedImages.remove(imagePath);
77                     viewHolder.imageView.setColorFilter(null);
78                     viewHolder.checkBtn.setImageResource(R.mipmap.img_picture_unselected);
79                 } else { // 如果图片没有被选择,则选择图片
80                     selectedImages.add(imagePath);
81                     viewHolder.imageView.setColorFilter(Color.parseColor("#77000000")); // 设置图片上面覆盖一层透明的黑色蒙版
82                     viewHolder.checkBtn.setImageResource(R.mipmap.img_pictures_selected); // 设置多选按钮为选中
83                 }
84             }
85         });
86 
87         return convertView;
88     }
89 
90     private class ViewHolder {
91         ImageView imageView;
92         ImageButton checkBtn;
93     }
94 }

主界面GridView的子View布局sideworks_griditem.xml文件中的代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5 
 6     <ImageView
 7         android:id="@+id/find_griditem_iv_image"
 8         android:layout_width="match_parent"
 9         android:layout_height="100.0dip"
10         android:scaleType="centerCrop"
11         android:src="@mipmap/img_pictures_notfound" />
12 
13     <ImageButton
14         android:id="@+id/find_griditem_ib_check"
15         android:layout_width="wrap_content"
16         android:layout_height="wrap_content"
17         android:layout_alignParentRight="true"
18         android:layout_marginRight="3.0dip"
19         android:layout_marginTop="3.0dip"
20         android:background="@null"
21         android:clickable="false"
22         android:src="@mipmap/img_picture_unselected" />
23 
24 </RelativeLayout>

弹出窗口DirsPopWindow.java中的代码:

  1 package com.itgungnir.view;
  2 
  3 import android.content.Context;
  4 import android.graphics.drawable.BitmapDrawable;
  5 import android.util.DisplayMetrics;
  6 import android.view.LayoutInflater;
  7 import android.view.MotionEvent;
  8 import android.view.View;
  9 import android.view.ViewGroup;
 10 import android.view.WindowManager;
 11 import android.widget.AdapterView;
 12 import android.widget.ArrayAdapter;
 13 import android.widget.ImageView;
 14 import android.widget.ListView;
 15 import android.widget.PopupWindow;
 16 import android.widget.TextView;
 17 
 18 import com.itgungnir.activity.R;
 19 import com.itgungnir.entity.FolderModel;
 20 import com.itgungnir.tools.ImageLoader;
 21 
 22 import java.util.List;
 23 
 24 public class DirsPopWindow extends PopupWindow {
 25     private int width; // PopUpWindow的宽度
 26     private int height; // PopUpWindow的高度
 27     private View convertView; // 代表PopUpWindow
 28     private ListView dirList; // 显示目录信息的ListView
 29     private List<FolderModel> datas; // ListView中显示的信息的列表
 30 
 31     public OnDirSelectListener listener;
 32 
 33     public interface OnDirSelectListener {
 34         void onDirSelected(FolderModel folder);
 35     }
 36 
 37     public void setOnDirSelectListener(OnDirSelectListener listener) {
 38         this.listener = listener;
 39     }
 40 
 41     public DirsPopWindow(Context context, List<FolderModel> datas) {
 42         calculateWidthAndHeight(context);
 43         convertView = LayoutInflater.from(context).inflate(R.layout.sideworks_popwindow, null);
 44         this.datas = datas;
 45         setContentView(convertView);
 46         setWidth(this.width);
 47         setHeight(this.height);
 48         setFocusable(true);
 49         setTouchable(true);
 50         setOutsideTouchable(true); // 外面可以点击
 51         setBackgroundDrawable(new BitmapDrawable()); // 点击外面的区域会使PopUpWindow消失
 52         // 点击外部的事件(让PopUpWindow消失)
 53         setTouchInterceptor(new View.OnTouchListener() {
 54             @Override
 55             public boolean onTouch(View v, MotionEvent event) {
 56                 if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
 57                     dismiss();
 58                     return true;
 59                 }
 60                 return false;
 61             }
 62         });
 63 
 64         initView(context);
 65         initEvent();
 66     }
 67 
 68     private void initView(Context context) {
 69         dirList = (ListView) convertView.findViewById(R.id.find_popwindow_lv_dirs);
 70         dirList.setAdapter(new DirAdapter(context, datas));
 71     }
 72 
 73     private void initEvent() {
 74         dirList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 75             @Override
 76             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 77                 if (listener != null) {
 78                     listener.onDirSelected(datas.get(position));
 79                 }
 80             }
 81         });
 82     }
 83 
 84     /**
 85      * 计算PopUpWindow的宽度和高度
 86      */
 87     private void calculateWidthAndHeight(Context context) {
 88         // 获取屏幕的宽和高
 89         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
 90         DisplayMetrics metrics = new DisplayMetrics();
 91         wm.getDefaultDisplay().getMetrics(metrics);
 92         // 为PopUpWindow设置宽和高
 93         this.width = metrics.widthPixels;
 94         this.height = (int) (metrics.heightPixels * 0.7);
 95     }
 96 
 97     /**
 98      * PopUpWindow中ListView的适配器
 99      */
100     private class DirAdapter extends ArrayAdapter<FolderModel> {
101         private LayoutInflater inflater;
102         private List<FolderModel> datas;
103 
104         public DirAdapter(Context context, List<FolderModel> objects) {
105             super(context, 0, objects);
106             inflater = LayoutInflater.from(context);
107         }
108 
109         @Override
110         public View getView(int position, View convertView, ViewGroup parent) {
111             ViewHolder viewHolder = null;
112             if (convertView == null) {
113                 viewHolder = new ViewHolder();
114                 convertView = inflater.inflate(R.layout.sideworks_popitem, parent, false);
115                 viewHolder.image = (ImageView) convertView.findViewById(R.id.find_popitem_iv_image);
116                 viewHolder.dirName = (TextView) convertView.findViewById(R.id.find_popitem_tv_dir);
117                 viewHolder.imgCount = (TextView) convertView.findViewById(R.id.find_popitem_tv_imgcount);
118                 convertView.setTag(viewHolder);
119             } else {
120                 viewHolder = (ViewHolder) convertView.getTag();
121             }
122 
123             FolderModel folder = getItem(position);
124             // 重置
125             viewHolder.image.setImageResource(R.mipmap.img_pictures_notfound);
126             // 回调加载
127             ImageLoader.getInstance(3, ImageLoader.Type.LIFO).loadImage(folder.getFirstImgPath(), viewHolder.image);
128             viewHolder.dirName.setText(folder.getName());
129             viewHolder.imgCount.setText(folder.getCount() + "");
130 
131             return convertView;
132         }
133 
134         private class ViewHolder {
135             ImageView image;
136             TextView dirName;
137             TextView imgCount;
138         }
139     }
140 }

弹出窗口的布局文件sideworks_popwindow.xml中的代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical">
 6 
 7     <ListView
 8         android:id="@+id/find_popwindow_lv_dirs"
 9         android:layout_width="match_parent"
10         android:layout_height="match_parent"
11         android:background="@color/cl_white"
12         android:cacheColorHint="@color/cl_transparent"
13         android:divider="#EEE3D9"
14         android:dividerHeight="1.0dip" />
15 
16 </LinearLayout>

弹出窗口中ListView的子View的布局sideworks_popitem.xml文件中的代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="120.0dip"
 5     android:background="@color/cl_white"
 6     android:padding="10.0dip">
 7 
 8     <ImageView
 9         android:id="@+id/find_popitem_iv_image"
10         android:layout_width="100.0dip"
11         android:layout_height="100.0dip"
12         android:layout_centerVertical="true"
13         android:background="@mipmap/img_pic_dir_bg"
14         android:contentDescription="@string/app_name"
15         android:paddingBottom="17.0dip"
16         android:paddingLeft="12.0dip"
17         android:paddingRight="12.0dip"
18         android:paddingTop="9.0dip"
19         android:scaleType="fitXY" />
20 
21     <LinearLayout
22         android:layout_width="wrap_content"
23         android:layout_height="wrap_content"
24         android:layout_centerVertical="true"
25         android:layout_marginLeft="20.0dip"
26         android:layout_toRightOf="@+id/find_popitem_iv_image"
27         android:orientation="vertical">
28 
29         <TextView
30             android:id="@+id/find_popitem_tv_dir"
31             android:layout_width="wrap_content"
32             android:layout_height="wrap_content"
33             android:textColor="@color/cl_black"
34             android:textSize="16.0sp" />
35 
36         <TextView
37             android:id="@+id/find_popitem_tv_imgcount"
38             android:layout_width="wrap_content"
39             android:layout_height="wrap_content"
40             android:textColor="@android:color/darker_gray"
41             android:textSize="14.0sp" />
42     </LinearLayout>
43 
44     <ImageView
45         android:layout_width="wrap_content"
46         android:layout_height="wrap_content"
47         android:layout_alignParentRight="true"
48         android:layout_centerVertical="true"
49         android:src="@mipmap/img_dir_choosen" />
50 
51 </RelativeLayout>

弹出窗口弹出的动画slide_up.xml文件中的代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <set xmlns:android="http://schemas.android.com/apk/res/android">
 3 
 4     <translate
 5         android:duration="200"
 6         android:fromXDelta="0"
 7         android:fromYDelta="100%"
 8         android:toXDelta="0"
 9         android:toYDelta="0" />
10 
11 </set>

弹出窗口隐藏的动画slide_down.xml文件中的代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <set xmlns:android="http://schemas.android.com/apk/res/android">
 3 
 4     <translate
 5         android:duration="200"
 6         android:fromXDelta="0"
 7         android:fromYDelta="0"
 8         android:toXDelta="0"
 9         android:toYDelta="100%" />
10 
11 </set>

样式文件styles.xml中的代码:

 1 <resources>
 2 
 3     <!-- Base application theme. -->
 4     <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
 5         <!-- Customize your theme here. -->
 6         <item name="colorPrimary">@color/colorPrimary</item>
 7         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
 8         <item name="colorAccent">@color/colorAccent</item>
 9     </style>
10 
11     <style name="Style_Main_TextView">
12         <item name="android:layout_width">wrap_content</item>
13         <item name="android:layout_height">wrap_content</item>
14         <item name="android:layout_centerVertical">true</item>
15         <item name="android:textColor">@android:color/white</item>
16         <item name="android:textSize">18.0sp</item>
17         <item name="android:textStyle">bold</item>
18     </style>
19 
20     <style name="Style_PopWindow_Anim">
21         <item name="android:windowEnterAnimation">@anim/slide_up</item>
22         <item name="android:windowExitAnimation">@anim/slide_down</item>
23     </style>
24 
25 </resources>

清单文件AndroidMenifest.xml文件中的代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.itgungnir.activity">
 4 
 5     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 6     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 7 
 8     <application
 9         android:allowBackup="true"
10         android:icon="@mipmap/ic_launcher"
11         android:label="@string/app_name"
12         android:supportsRtl="true"
13         android:theme="@android:style/Theme.NoTitleBar">
14         <activity android:name=".MainActivity">
15             <intent-filter>
16                 <action android:name="android.intent.action.MAIN" />
17 
18                 <category android:name="android.intent.category.LAUNCHER" />
19             </intent-filter>
20         </activity>
21     </application>
22 
23 </manifest>

 

转载于:https://www.cnblogs.com/blog-wzy/p/5403246.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/373221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

loss function

什么是loss? loss: loss是我们用来对模型满意程度的指标。loss设计的原则是&#xff1a;模型越好loss越低&#xff0c;模型越差loss越高&#xff0c;但也有过拟合的情况。     loss function: 在分类问题中&#xff0c;输入样本经过含权重矩阵θ的模型后会得出关于各个类别…

oracle查询表的id,oracle 查看所有用户及密码 实现Oracle查询用户所有表

1、oracle 查看所有用户及密码SQL> select username from dba_users;2、 实现Oracle查询用户所有表下面为您介绍的语句用于实现Oracle查询用户所有表&#xff0c;如果您对oracle查询方面感兴趣的话&#xff0c;不妨一看。select * from all_tab_comments-- 查询所有用户的表…

FindBugs和JSR-305

假设那组开发人员在大型项目的各个部分上并行工作–一些开发人员在进行服务实现&#xff0c;而其他开发人员在使用该服务的代码。 考虑到API的假设&#xff0c;两个小组都同意服务API&#xff0c;并开始单独工作。 您认为这个故事会有幸福的结局吗&#xff1f; 好吧&#xff0c…

什么是Akka?

在深入研究什么是Akka之前&#xff0c;让我们退后一步来了解并发编程的概念在应用程序开发世界中是如何演变的。 应用程序已经从大型的整体程序演变为面向对象的模型。 随着Java EE和Spring框架的出现&#xff0c;应用程序设计演变为更多的基于流程或任务的设计模型。 EJB或Poj…

Unity3d 实现顶点动画

在今年GDC上发现一个非常有趣的演讲&#xff0c;叫做Animating With Math&#xff0c;遂实现之&#xff0c;是讲述顶点shader动画的&#xff0c;举了几个经典的例子&#xff0c;但是讲者并没有给代码&#xff0c;而是像虚幻引擎那样的节点&#xff0c;这样更加清楚明了之前博主…

windows2012同步linux时间,Windows server2012时间同步NTP配置

遇到经常服务器时间无法同步&#xff0c;可以自己建立一台时间同步服务器&#xff0c;NTP配置如下&#xff1a;一、服务端配置 (Ntp服务器&#xff0c;客户端将根据这台服务器的时间进行同步)1、微软键R键&#xff0c;进入“运行”&#xff0c;输入“regedit”,进入注册表2、 H…

Java EE 6示例– Galleria第2部分

您可能在最后一篇Java EE 6 Galleria示例帖子中关注了我。 第一个是基本介绍。 第二个是关于在最新的GlassFish上运行它。 有人提到RedHat&#xff0c;我们应该研究将这个示例从GlassFish中移除。 很好;&#xff09;感谢您的好主意。 这正是我们今天要做的。 我将把Galleria示例…

与reCAPTCHA的Spring集成

有时我们只需要CAPTCHA &#xff0c;这是一个可悲的事实。 今天&#xff0c;我们将学习如何与reCAPTCHA集成。 因为主题本身并不是特别有趣和高级&#xff0c;所以我们将通过使用Spring Integration处理低级细节来过度设计&#xff08;&#xff1f;&#xff09;。 Google决定使…

《机器学习基石》---感知机算法

1 推导感知机模型 基本思想是&#xff0c;把特征的线性加权值作为一个分数&#xff0c;根据这个分数与一个门限值的关系来进行分类&#xff1a; 我们加一个特征x0等于1&#xff0c;门限值就可以放到w里面去&#xff0c;得到更简单的形式&#xff1a; 这就是感知机模型&#xff…

Python之路【第八篇】:堡垒机实例以及数据库操作

Python之路【第八篇】&#xff1a;堡垒机实例以及数据库操作 堡垒机前戏 开发堡垒机之前&#xff0c;先来学习Python的paramiko模块&#xff0c;该模块机遇SSH用于连接远程服务器并执行相关操作 SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接&#xff1a; 12…

ADF BC:创建绑定到业务组件的UI表

在此示例中&#xff0c;我们将展示如何创建绑定到业务组件的简单UI表&#xff08;af&#xff1a;table&#xff09;。 我再次尝试使用简单的标准在网上进行搜索&#xff1a; “如何创建绑定到业务组件ADF 11g的af&#xff1a;table” 我必须承认我没有得到我想要的答案。 信息…

MyBaits 错误分析

错误原因&#xff1a;在DAO的映射文件中&#xff0c;在映射标签中的type类型写成DAO类了&#xff0c;应该写成javaBean转载于:https://www.cnblogs.com/shuaiandjun/p/5428847.html

斑马打印机linux驱动安装教程,linux-Zebra软件包的基本安装与配置

Zebra是一个路由软件包&#xff0c;提供基于TCP/IP路由服务&#xff0c;支持RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3, BGP- 4,和 BGP-4等众多路由协议。Zebra还支持BGP特性路由反射器(Route Reflector)。除了传统的 IPv4路由协议&#xff0c;Zebra也支持IPv6路由协议。如果运行的…

Java 7对抑制异常的支持

在JDK 7中 &#xff0c;向Throwable类&#xff08; Exception和Error类的父类&#xff09;添加了一个新的构造函数和两个新方法。 添加了新的构造函数和两个新方法以支持“抑制的异常”&#xff08;不要与吞咽或忽略异常的不良做法相混淆&#xff09;。 在本文中&#xff0c;我…

易于使用的单位和集成代码

此示例说明如何使用Maven和Sonar生成单元测试和集成测试的覆盖率。 它使用非常简单的技术&#xff0c;只需10-15分钟即可在任何现有的Maven构建中运行。 它可用于单元&#xff0c;集成&#xff0c;ATDD或任何其他类型的测试套件。 覆盖率结果显示在Sonar中。 有什么事吗&#x…

Ubuntu 16.04 安装 VMware-Workstation-12

以前一直使用 Ubuntu Virtaulbox &#xff0c;最近测试了 VMware-Workstation-9,性能超过 Virtaulbox-4.2.x,下面是详细步骤:1 首先准备一个Ubuntu 系统 lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04 LTS Release: 16.04 …

SSH实战 · 唯唯乐购项目(中)

用户模块三&#xff1a;一级分类的查询创建一级分类表并导入基本数据CREATE TABLE category (cid int(11) NOT NULL AUTO_INCREMENT,cname varchar(255) DEFAULT NULL,PRIMARY KEY (cid)) ENGINEInnoDB AUTO_INCREMENT11 DEFAULT CHARSETutf8;建包及相应的类:com.weiwei.shoppi…

Android的IPC机制(一)——AIDL的使用

综述 IPC(interprocess communication)是指进程间通信&#xff0c;也就是在两个进程间进行数据交互。不同的操作系统都有他们自己的一套IPC机制。例如在Linux操作系统中可以通过管道、信号量、消息队列、内存共享、套接字等进行进程间通信。那么在Android系统中我们可以通过Bin…

Netty:透明地使用SPDY和HTTP

大多数人已经从谷歌那里听说过SPDY&#xff0c;该协议被提议作为老化的HTTP协议的替代品。 Web服务器是浏览器正在缓慢地实现该协议&#xff0c;并且支持正在增长。 在最近的文章中&#xff0c;我已经写过SPDY的工作方式以及如何在Jetty中启用SPDY支持。 由于Netty&#xff08;…

selenium 等待页面加载完成

一、隐形加载等待&#xff1a; file:///C:/Users/leixiaoj/Desktop/test.html 该页面负责创建一个div <html> <head><title>Set Timeout</title><style>.red_box {background-color: red; width 20%; height:100px; border: none;}</style&…