今天一天没事,研究一个瀑布效果图片浏览器安卓源码。下面是个人理解:
运行效果
先说XML布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/background_light"
android:orientation="vertical" >
<!-- -->
<include
android:id="@+id/progressbar"
layout="@layout/loading" />
<com.jj.waterfall.LazyScrollView
android:id="@+id/lazyscrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:scrollbars="@null" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/background_light"
android:orientation="horizontal"
android:padding="2dp" >
<LinearLayout
android:id="@+id/layout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/layout02"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/layout03"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
</LinearLayout>
</com.jj.waterfall.LazyScrollView>
<TextView
android:id="@+id/loadtext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/loading_bg"
android:gravity="center"
android:padding="10dp"
android:text="Loading..."
android:textColor="@android:color/background_dark" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center" >
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
上面的布局是程序进去时显示的一个进度条,一个自定义View(瀑布图片) ,还有就是一个文本显示Loading的文本,当向下滑动屏幕时,图片还没有加载时,会显示。
下面看程序:
MainActivity.java
package com.jj.waterfall;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import android.app.Activity;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends Activity implements
LazyScrollView.OnScrollListener {
/** Called when the activity is first created. */
private LinearLayout layout01, layout02, layout03;// 3列
private List<String> image_filenames; // 图片集合
private ImageDownLoadAsyncTask asyncTask;
private AssetManager assetManager;
private int Image_width;// 图片显示的宽度
private int x, y;// 行,列
private int current_page = 0;// 页码
private int count = 15;// 每页显示的个数
private LazyScrollView lazyScrollView;// 自定义scrollview
private LinearLayout progressbar;// 进度条
private TextView loadtext;// 底部加载view
private String tag = "jj";
/***
* init view
*/
public void InitView() {
setContentView(R.layout.main);
lazyScrollView = (LazyScrollView) findViewById(R.id.lazyscrollview);
progressbar = (LinearLayout) findViewById(R.id.progressbar);
loadtext = (TextView) findViewById(R.id.loadtext);
lazyScrollView.getView();
lazyScrollView.setOnScrollListener(this);
layout01 = (LinearLayout) findViewById(R.id.layout01);
layout02 = (LinearLayout) findViewById(R.id.layout02);
layout03 = (LinearLayout) findViewById(R.id.layout03);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InitView();
assetManager = this.getAssets();
// 获取显示图片宽度
Image_width = (getWindowManager().getDefaultDisplay().getWidth() - 4) / 3;
try {
image_filenames = Arrays.asList(assetManager.list("images"));// 获取图片名称
} catch (IOException e) {
e.printStackTrace();
}
addImage(current_page, count);
}
/***
* 添加imageview 到layout
*
* @param imagePath
* 图片name
* @param j
* 列
* @param i
* 行
*/
public void addBitMapToImage(String imagePath, int j, int i) {
ImageView imageView = getImageview();
asyncTask = new ImageDownLoadAsyncTask(this, imagePath, imageView,
Image_width);
asyncTask.setProgressbar(progressbar);
asyncTask.setLoadtext(loadtext);
asyncTask.execute();
imageView.setTag(i);
if (j == 0) {
layout01.addView(imageView);
} else if (j == 1) {
layout02.addView(imageView);
} else if (j == 2) {
layout03.addView(imageView);
}
// imageView.setOnClickListener(new OnClickListener() {
//
// @Override
// public void onClick(View v) {
// Toast.makeText(MainActivity.this,
// "您点击了" + v.getTag() + "个Item", Toast.LENGTH_SHORT)
// .show();
//
// }
// });
}
/***
*
* 创建显示imageview setScaleType 详解: CENTER /center
* 按图片的原来size居中显示,当图片长/宽超过View的长/宽,则截 取图片的居中部分显示 CENTER_CROP / centerCrop
* 按比例扩大图片的size居中显示,使得图片长 (宽)等于或大于View的长(宽) CENTER_INSIDE / centerInside
* 将图片的内容完整居中显示,通过按比例缩小 或原来的size使得图片长/宽等于或小于View的长/宽 FIT_CENTER / fitCenter
* 把图片按比例扩大/缩小到View的宽度,居中显示 FIT_END / fitEnd 把
* 图片按比例扩大/缩小到View的宽度,显示在View的下部分位置 FIT_START / fitStart 把
* 图片按比例扩大/缩小到View的宽度,显示在View的上部分位置 FIT_XY / fitXY 把图片 不按比例 扩大/缩小到View的大小显示
* MATRIX / matrix 用矩阵来绘制
*
* @return
*/
public ImageView getImageview() {
// 创建显示图片的对象
ImageView imageView = new ImageView(this);
LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(layoutParams);
imageView.setPadding(2, 0, 2, 2);// 设置间距
return imageView;
}
@Override
public void onBottom() {
addImage(++current_page, count);
}
/***
* 加载更多
*
* @param current_page
* 当前页数
* @param count
* 每页显示个数
*/
private void addImage(int current_page, int count) {
for (int x = current_page * count; x < (current_page + 1) * count
&& x < image_filenames.size(); x++) {
addBitMapToImage(image_filenames.get(x), y, x);
y++;
if (y >= 3)
y = 0;
}
}
@Override
public void onTop() {
Log.d(tag, "top");
}
@Override
public void onScroll() {
Log.d(tag, "scroll");
}
}
package com.jj.waterfall;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;
/***
* 自定义ScrollView
*
*/
public class LazyScrollView extends ScrollView {
private static final String tag = "LazyScrollView";
private Handler handler;
private View view;
public LazyScrollView(Context context) {
super(context);
}
public LazyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LazyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// 这个获得总的高度
public int computeVerticalScrollRange() {
return super.computeHorizontalScrollRange();
}
public int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
/***
* 初始化
*/
private void init() {
this.setOnTouchListener(onTouchListener);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// process incoming messages here
super.handleMessage(msg);
switch (msg.what) {
case 1:
if (view.getMeasuredHeight() <= getScrollY() + getHeight()) {
if (onScrollListener != null) {
onScrollListener.onBottom();
}
} else if (getScrollY() == 0) {
if (onScrollListener != null) {
onScrollListener.onTop();
}
} else {
if (onScrollListener != null) {
onScrollListener.onScroll();
}
}
break;
default:
break;
}
}
};
}
OnTouchListener onTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
if (view != null && onScrollListener != null) {
handler.sendMessageDelayed(handler.obtainMessage(1), 200);
}
break;
default:
break;
}
return false;
}
};
/**
* 获得参考的View,主要是为了获得它的MeasuredHeight,然后和滚动条的ScrollY+getHeight作比较。
*/
public void getView() {
this.view = getChildAt(0);
if (view != null) {
init();
}
}
/**
* 定义接口
*
* @author admin
*
*/
public interface OnScrollListener {
void onBottom();
void onTop();
void onScroll();
}
private OnScrollListener onScrollListener;
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
}
ImageDownLoadAsyncTask.java
package com.jj.waterfall;
import java.io.IOException;
import java.io.InputStream;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* 异步下载图片
*
*/
public class ImageDownLoadAsyncTask extends AsyncTask<Void, Void, Bitmap> {
private String imagePath;
private ImageView imageView;
private Context context;
private AssetManager assetManager;
private int Image_width;// 显示图片的宽度
private final String file = "images/";
private LinearLayout progressbar;
private TextView loadtext;
/**
* AsyncTask初始化
* @param context 前台Context
* @param imagePath 图片path
* @param imageView 前台ImageView
*/
public ImageDownLoadAsyncTask(Context context, String imagePath,
ImageView imageView, int Image_width) {
this.imagePath = imagePath;
this.imageView = imageView;
this.context = context;
assetManager = this.context.getAssets();
this.Image_width = Image_width;
}
public void setLoadtext(TextView loadtext) {
this.loadtext = loadtext;
}
public void setProgressbar(LinearLayout progressbar) {
this.progressbar = progressbar;
}
/*
* 这里的Integer参数对应AsyncTask中的第一个参数
* 这里的String返回值对应AsyncTask的第三个参数
* 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
* 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
*
*/
@Override
protected Bitmap doInBackground(Void... params) {
publishProgress();
try {
InputStream inputStream = assetManager.open(file + imagePath);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/*
*
* 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
* 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
*
*/
@Override
protected void onPostExecute(Bitmap drawable) {
super.onPostExecute(drawable);
if (drawable != null) {
LayoutParams layoutParams = imageView.getLayoutParams();
int height = drawable.getHeight();// 获取图片的高度.
int width = drawable.getWidth();// 获取图片的宽度
layoutParams.height = (height * Image_width) / width;
imageView.setLayoutParams(layoutParams);
imageView.setImageBitmap(drawable);
}
if (progressbar.isShown() || loadtext.isShown()) {
progressbar.setVisibility(View.GONE);
loadtext.setVisibility(View.GONE);
}
}
/*
* 该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
if (!loadtext.isShown()) {
loadtext.setVisibility(View.VISIBLE);
}
}
/*
* 这里的Intege参数对应AsyncTask中的第二个参数
* 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
* onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
*/
protected void onProgressUpdate(Integer... values) {
loadtext.setText(imageView.getTag().toString());
}
}
程序开始进入onCreate,通过调getAssets().list("images")取得所有图片的url,相当于PC机上的磁盘位置。
在此之前,程序没有任何图片,显示一个进度条,因为ProgressBar把所有布局覆盖了。然后调用addImage方法,
显示第一页图片,调用addBitMapToImage方法,通过AsyncTask取得图片,事实上传入本Acitivity的ImageView引用给
ImageDownLoadAsyncTask,然后ImageDownLoadAsyncTask执行下载图片,再通过.setImageBitmap把ImageView设置图片。
ImageDownLoadAsyncTask.execute调用时,执行onPreExecute,在主页面下面显示Loading进度条,然后调用doInBackground,
执行完后调用onPostExecute,隐藏Loading进度条。至此,页面完成初始化。
重要的部分,屏幕向上滑动,下面的图片怎么显示出来,
lazyScrollView.setOnScrollListener(this);自定义View定义了一个屏幕滑动事件OnScrollListener,OnScrollListener定义在LazyScrollView里面,这是一种面向对象的程序设计。因为LazyScrollView依赖LazyScrollView存在。当屏幕向下滑动,调用addBitMapToImage,又重新建了一个ImageDownLoadAsyncTask,相当于启动了一个进程来更新页面。自定义VIEW页面有三列,每次向一列中加一张图片。至此瀑布效果图片浏览实现。
本人才疏学浅,学习笔记,请勿见笑。