Android学习之打造美女图片浏览器

本文旨在教你打造一个真实的一个图片浏览器,并非写一堆链接用来测试;也算是用到Android基本的常用的知识,对于初学者来说是一个不错的练手demo;当然本文对于图片加载也有自己的一些见解,希望可以帮助到各位;

 

                 


                

 

首先在开始讲解前要感谢一下github开源社区给了我很多帮助,相信不少人也从这里获取到自己想要的东西,当然如果有能力也是希望可以回馈社区;

再此列出在项目中用到的开源框架:

网络访问框架volley,这个大概大家都知道无需解释:

https://github.com/mcxiaoke/android-volley详细学习请移至http://hukai.me/android-training-course-in-chinese/connectivity/volley/index.html


图片加载框架universal-image-loader这个谁用谁知道,当然也有不少陷阱:

https://github.com/nostra13/Android-Universal-Image-Loader,详细学习请移至http://codekk.com/open-source-project-analysis


下拉刷新框架android-Ultra-Pull-to-Refresh使用简单定制方便:

https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh


RippleEffect一个实现Material Design Ripple效果的库:

https://github.com/traex/RippleEffect,其实你仔细看他的实现相信你也可以做到


Stackblur高斯模糊框架,我在上一篇博客中有提到高斯模糊制作启动界面

https://github.com/kikoso/android-stackblur


Gson解决对象和json的转换,方便简单快速:

https://github.com/google/gson,项目中虽然没有用到,因为字段比较少就省了,但是还是建议去用用


当然还得介绍一下android-open-project,进去看了就明白

https://github.com/Trinea/android-open-project

 

好了该感谢的都感谢完了,下面切入我们的正题;首先要解决的问题就是数据,app没有数据总是华而不实的,当然这里也不是要我们自己写服务器;

数据来源:百度图片api

http://image.baidu.com/channel/listjson?pn=0&rn=30&tag1=美女&tag2=全部,打开链接可以看到返回的json数据

参数详细介绍

pn:从第几项开始加载

rn:加载多少项

tag1:大分类(既然是美女图,那么这里就填美女喽)

tag2:小分类(例如:清纯,性感,气质...等等,可以去百度图片看看分类)

上面的链接解释就是:从第0项加载30个所有美女类型的图片;

如果要加载第二页呢?pn=30,其他不变;详细可以移至http://blog.csdn.net/yuanwofei/article/details/16343743,不过这篇博客有些时候了,可能用法上有误,pn不是页数;

 

项目结构及分包:

如果是为了测试其实倒也无所谓,但是这里我还是要做到一定的规范;正所谓写代码很容易,写好却不容易:

包结构图


 

  • .util:存放app所需的工具类,这个模块不只是这一个项目会用到,大多数项目都可以复用。
  • .view:自定义view存放目录
  • .app:存放application类,app的入口用于初始化全局的一些东西

其他包就不多加解释了;

 

界面布局

欢迎界面

欢迎界面的高斯模糊效果请移至高斯模糊制作启动界面

主界面

头部是一个ToolbarToolbar右侧是一个menu用于弹出美女类型的popwindow

内容区域是一个GirdView,附在下拉刷新布局中;

查看大图界面

一个viewpager,每个view是一个ImageView

界面布局大致就是这样,是不是感觉很简单;

 

主界面:

Activity布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include layout="@layout/toolbar" />

    <FrameLayout
        android:id="@+id/fragmentContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>


Toolbar

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.xiaozhi.beautygallery"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary"
    android:minHeight="?attr/actionBarSize"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    android:visibility="gone"
    android:theme="@style/ThemeOverlay.AppCompat.ActionBar" >

</android.support.v7.widget.Toolbar>

Fragment布局

<?xml version="1.0" encoding="utf-8"?>
<in.srain.cube.views.ptr.PtrFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cube_ptr="http://schemas.android.com/apk/res-auto"
    android:id="@+id/rotate_header_grid_view_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background_material_dark"
    cube_ptr:ptr_duration_to_close="200"
    cube_ptr:ptr_duration_to_close_header="1000"
    cube_ptr:ptr_keep_header_when_refresh="true"
    cube_ptr:ptr_pull_to_fresh="false"
    cube_ptr:ptr_ratio_of_header_height_to_refresh="1.2"
    cube_ptr:ptr_resistance="1.7" >

    <GridView
        android:id="@+id/gridView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fadingEdge="none"
        android:focusable="false"
        android:horizontalSpacing="@dimen/image_padding"
        android:listSelector="@null"
        android:numColumns="2"
        android:verticalSpacing="@dimen/image_padding" />

</in.srain.cube.views.ptr.PtrFrameLayout>

GridView item布局

<?xml version="1.0" encoding="utf-8"?>
<com.xiaozhi.beautygallery.view.RippleView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ripple="http://schemas.android.com/apk/res-auto"
    android:id="@+id/rect"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    ripple:rv_alpha="50"
    ripple:rv_rippleDuration="100"
    ripple:rv_type="rectangle"
    ripple:rv_zoom="true"
    ripple:rv_zoomDuration="150" >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="@dimen/image_height"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop" />

</com.xiaozhi.beautygallery.view.RippleView>

GridView底部布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/foot_view_layout"
    android:layout_width="fill_parent"
    android:layout_height="50dp"
    android:layout_columnSpan="2"
    android:gravity="center"
    android:orientation="horizontal"
    android:background="@drawable/footer_bg">

    <ProgressBar
        android:id="@+id/footer_loading"
        style="@android:style/Widget.ProgressBar.Small.Inverse"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <TextView
        android:id="@+id/footview_text"
        android:layout_width="wrap_content"
        android:layout_height="50dip"
        android:layout_gravity="center"
        android:gravity="center"
        android:textColor="@android:color/white"
        android:textSize="15sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/footview_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="5dp"
        android:background="@null"
        android:text="refresh" />

</LinearLayout>


Popwindow布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/pop_win_bg"
    android:orientation="vertical"
    android:padding="@dimen/image_padding" >

    <GridView
        android:id="@+id/popGridView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fadingEdge="none"
        android:focusable="false"
        android:gravity="center"
        android:horizontalSpacing="@dimen/image_padding"
        android:listSelector="@drawable/shape_more_pop"
        android:numColumns="5"
        android:verticalSpacing="@dimen/image_padding" />

</LinearLayout>

实现

SingleFragmentActivity所有以fragment为布局的activity继承该类:

public abstract class SingleFragmentActivity extends AppCompatActivity {

	protected abstract Fragment createFragment();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 获取FragmentManager对象
		FragmentManager fm = getSupportFragmentManager();
		// 获取fragment
		Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

		// fragment事务
		if (fragment == null) {
			fragment = createFragment();
			fm.beginTransaction().add(R.id.fragmentContainer, fragment)
					.commit();
		}
	}

}

MainActivity主界面activity

public class MainActivity extends SingleFragmentActivity {

	private Toolbar mToolbar;
	private MorePopWindow mMorePopWindow;
	private Fragment mFragment;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		initViews();
	}

	protected void initViews() {
		mToolbar = (Toolbar) findViewById(R.id.toolbar);
		mToolbar.setVisibility(View.VISIBLE);
		setSupportActionBar(mToolbar);
		mMorePopWindow = new MorePopWindow(this);
		mMorePopWindow
				.setOnMorePopWindowItemClickListener(new OnMorePopWindowItemClickListener() {

					@Override
					public void onItemClick(int position, String item) {
						if (mFragment instanceof FragmentMain) {
							((FragmentMain) mFragment)
									.changeOtherBeautyType(item);
						}
					}
				});
		mToolbar.setOnMenuItemClickListener(new OnMenuItemClickListener() {

			@Override
			public boolean onMenuItemClick(MenuItem item) {
				switch (item.getItemId()) {
				case R.id.action_more:
					showMorePopWindow();
					break;
				default:
					break;
				}
				return false;
			}
		});

	}

	protected void showMorePopWindow() {
		// 显示窗口
		mMorePopWindow.showAsDropDown(mToolbar);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_more) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	protected Fragment createFragment() {
		mFragment = new FragmentMain();
		return mFragment;
	}
}

FragmentMain显示图片列表fragment

public class FragmentMain extends Fragment {

	private static final String TAG = "FragmentMain";
	/** 从第几项开始加载图片*/
	private int pn;
	private String tag2;
	private String oldTag2;

	private GridView mGridView;
	private MyGridViewAdapter mAdapter;
	public static List<Image> mListImages = new ArrayList<Image>();
	private View view;
	private PtrFrameLayout mFrame;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		view = inflater.inflate(R.layout.fragment_main, container, false);

		initViews();
		return view;
	}

	private void initViews() {
		tag2 = CustomUtil.getInstance().getString(VolleyUtil.TAG2);
		if (tag2 == null) {
			tag2 = VolleyUtil.TAG2_DEFAULT;
			CustomUtil.getInstance().save(VolleyUtil.TAG2, tag2);
		}
		oldTag2 = tag2;
		mGridView = (GridView) view.findViewById(R.id.gridView);
		mAdapter = new MyGridViewAdapter(getActivity(), mListImages);
		mAdapter.setLoadListener(new LoadListener() {

			@Override
			public void onLoad() {
				loadNextPage();
			}
		});
		mGridView.setAdapter(mAdapter);
		mFrame = (PtrFrameLayout) view
				.findViewById(R.id.rotate_header_grid_view_frame);
		StoreHouseHeader header = new StoreHouseHeader(getActivity());
		header.setPadding(0, 15, 0, 15);
		header.initWithString("Loading...");

		mFrame.setDurationToCloseHeader(1500);
		mFrame.setHeaderView(header);
		mFrame.addPtrUIHandler(header);
		mFrame.postDelayed(new Runnable() {
			@Override
			public void run() {
				mFrame.autoRefresh(false);
			}
		}, 100);
		mFrame.setPtrHandler(new PtrHandler() {
			@Override
			public boolean checkCanDoRefresh(PtrFrameLayout frame,
					View content, View header) {
				return PtrDefaultHandler.checkContentCanBePulledDown(frame,
						content, header);
			}

			@Override
			public void onRefreshBegin(PtrFrameLayout frame) {
				mListImages.clear();
				pn = 0;
				if (!oldTag2.equals(tag2)) {
					CustomUtil.getInstance().save(VolleyUtil.TAG2, tag2);
					oldTag2 = tag2;
				}
				loadItems();
			}
		});
	}

	/**
	 * 加载一页数据
	 */
	private void loadItems() {
		JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET,
				VolleyUtil.getInstance().getUrl(pn, tag2), null,
				new Listener<JSONObject>() {

					@Override
					public void onResponse(JSONObject jsonObject) {
						VolleyUtil.parseItems(mListImages, jsonObject);
						mFrame.refreshComplete();
						updateAdapter();
						mAdapter.setFooterViewStatus(FooterView.MORE);
					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError arg0) {
						Toast.makeText(getActivity(), R.string.loading_error,
								Toast.LENGTH_SHORT).show();
						mFrame.refreshComplete();
					}
				});
		VolleyUtil.getInstance().addToRequestQueue(request);
	}

	/**
	 * 加载下一页数据
	 */
	private void loadNextPage() {
		if (mAdapter != null) {
			mAdapter.setFooterViewStatus(FooterView.LOADING);
		}
		pn += VolleyUtil.PAGE_SIZE;
		loadItems();
	}

	private void updateAdapter() {
		mAdapter.notifyDataSetChanged();
	}

	public void changeOtherBeautyType(String tag2) {
		this.tag2 = tag2;
		mFrame.autoRefresh();
	}
}

MorePopWindow显示美女类型

public class MorePopWindow extends PopupWindow {

	private View mMoreView;
	private LayoutInflater mInflater;
	private Context mContext;
	private GridView mGridView;
	private ArrayAdapter<String> mAdapter;
	private List<String> mList;
	private String[] mBeautyTypes;
	private PopupWindow mPopupWindow;
	private int mCurrentPosition = 0;

	public MorePopWindow(Context context) {
		super(context);
		this.mContext = context;
		mPopupWindow = this;
		mInflater = LayoutInflater.from(mContext);
		mMoreView = mInflater.inflate(R.layout.view_more_pop, null);
		mGridView = (GridView) mMoreView.findViewById(R.id.popGridView);
		initStrings();
		mAdapter = new ArrayAdapter<String>(mContext,
				R.layout.view_more_pop_item, mBeautyTypes);
		mGridView.setAdapter(mAdapter);
		mGridView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				if (mOnMorePopWindowItemClickListener != null) {
					mOnMorePopWindowItemClickListener.onItemClick(position,
							mBeautyTypes[position]);
				}
				parent.getChildAt(mCurrentPosition).setBackgroundColor(
						Color.TRANSPARENT);
				view.setBackgroundResource(R.drawable.shape_more_pop);
				mCurrentPosition = position;
				mPopupWindow.dismiss();
				Log.d("More", "onItemClick:" + position);
			}
		});

		// 设置SelectPicPopupWindow的View
		this.setContentView(mMoreView);
		// 设置SelectPicPopupWindow弹出窗体的宽
		this.setWidth(LayoutParams.MATCH_PARENT);
		// 设置SelectPicPopupWindow弹出窗体的高
		this.setHeight(LayoutParams.WRAP_CONTENT);
		// 设置SelectPicPopupWindow弹出窗体可点击
		this.setFocusable(true);
		// 设置SelectPicPopupWindow弹出窗体动画效果
		 this.setAnimationStyle(R.style.popwin_anim_style);
		// 实例化一个ColorDrawable颜色为透明
		ColorDrawable dw = new ColorDrawable(0x55000000);
		// 设置SelectPicPopupWindow弹出窗体的背景
		this.setBackgroundDrawable(dw);
	}

	private void initStrings() {
		mBeautyTypes = mContext.getResources().getStringArray(
				R.array.beauty_array);
		mList = new ArrayList<String>();
		for (int i = 0; i < 20; i++) {
			mList.add("item" + i);
		}
	}

	public interface OnMorePopWindowItemClickListener {
		public void onItemClick(int position, String item);
	}

	private OnMorePopWindowItemClickListener mOnMorePopWindowItemClickListener;

	public void setOnMorePopWindowItemClickListener(
			OnMorePopWindowItemClickListener onMorePopWindowItemClickListener) {
		mOnMorePopWindowItemClickListener = onMorePopWindowItemClickListener;
	}
}

MyGridViewAdapter这个是核心,基本上所有的操作都和他有关

public class MyGridViewAdapter extends BaseAdapter {

	/** 图片视图类型 */
	public static final int VIEW_TYPE_ITEM = 0;
	/** 底部图片类型 */
	public static final int VIEW_TYPE_FOOT = 1;
	/** 视图数量 */
	public static final int VIEW_TYPE_COUNT = 2;

	private List<Image> mListImages;
	private Context mContext;
	private LayoutInflater mInflater;
	private FooterView mFooterView;

	public MyGridViewAdapter(Context context, List<Image> images) {
		this.mContext = context;
		this.mListImages = images;
		mInflater = LayoutInflater.from(mContext);
	}

	@Override
	public int getCount() {
		// 注意此处因为加了底部视图所以加1
		return mListImages.size() + 1;
	}

	@Override
	public Image getItem(int position) {
		return mListImages.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	/**
	 * 复写该方法使GridView正确缓存不同视图
	 */
	@Override
	public int getViewTypeCount() {
		return VIEW_TYPE_COUNT;
	}

	/**
	 * 复写该方法使GridView正确缓存不同视图
	 */
	@Override
	public int getItemViewType(int position) {
		if (position == getCount() - 1) {
			return VIEW_TYPE_FOOT;
		}
		return VIEW_TYPE_ITEM;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if (convertView == null) {
			convertView = initConvertView(position, convertView, parent);
		}

		setDataToConvertView(position, convertView);

		return convertView;
	}

	/**
	 * 给convertView设置数据
	 * 
	 * @param position
	 * @param convertView
	 */
	private void setDataToConvertView(int position, View convertView) {
		if (getItemViewType(position) == VIEW_TYPE_ITEM) {
			ViewHolder holder = (ViewHolder) convertView.getTag();
			Log.d("MyGrid", "position:" + position);
			holder.mImageView
					.setTag(R.id.imageView, getItem(position).getUrl());

			ImageLoaderUtil.displayImage(getItem(position).getUrl(), holder.mImageView);
		} else if (getItemViewType(position) == VIEW_TYPE_FOOT && position != 0) {
			setFooterViewStatus(FooterView.MORE);
		}
	}

	/**
	 * 初始化convertView
	 * 
	 * @param position
	 * @param convertView
	 * @param parent
	 * @return
	 */
	private View initConvertView(final int position, View convertView,
			ViewGroup parent) {
		ViewHolder holder = null;
		if (getItemViewType(position) == VIEW_TYPE_FOOT) {// 初始化底部加载更多视图
			mFooterView = new FooterView(mContext);
			convertView = mFooterView;
			mFooterView.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					if (mFooterView.getStatus() == FooterView.MORE
							&& mLoadListener != null) {
						mLoadListener.onLoad();
					}
				}
			});
			GridView.LayoutParams pl = new GridView.LayoutParams(
					MeasureUtil.getScreenSize(mContext)[0],
					LayoutParams.WRAP_CONTENT);
			mFooterView.setLayoutParams(pl);

			setFooterViewStatus(FooterView.HIDE);

		} else {// 初始化图片视图
			convertView = mInflater.inflate(R.layout.view_gallery_item, parent,
					false);
			holder = new ViewHolder();

			holder.mImageView = (ImageView) convertView
					.findViewById(R.id.imageView);
			holder.mImageView.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(final View v) {
					new Handler().postDelayed(new Runnable() {

						@Override
						public void run() {
							Intent intent = new Intent(mContext,
									BeautyPagerActivity.class);
							intent.putExtra(BeautyPagerActivity.POSITION,
									(Integer) v.getTag(R.id.gridView));
							mContext.startActivity(intent);
						}
					}, 200);
				}
			});
			convertView.setTag(holder);
		}
		return convertView;
	}

	class ViewHolder {
		ImageView mImageView;
	}

	/**
	 * 加载更多回调接口
	 */
	public interface LoadListener {
		void onLoad();
	}

	private LoadListener mLoadListener;

	public void setLoadListener(LoadListener loadListener) {
		mLoadListener = loadListener;
	}

	public void setFooterViewStatus(int status) {
		if (mFooterView != null) {
			mFooterView.setStatus(status);
		}
	}

}
上一篇博客中我讲到了Adapter中多布局缓存的问题,这次这里也用到了详细请移至Android学习之当ScrollView遭遇ListView(GridView)


好吧接下来我们梳理一遍流程

1.进入主界面也就是当FragmentMain初始化时调用loadItems()方法加载第一页数据并通过volley下载解析放到mListImages中;其实这个过程是通过PtrFrameLayout.autoRefresh()在回调接口中onRefreshBegin()实现刷新过程;

2.刷新MyGridViewAdapter显示图片,通过universal-image-loaderdisplayImage方法直接将urlimageview传入即可显示图片;也算是相当的简单了;(ps:可是坑才刚刚开始

3.我们可以点击popwindow中的美女类型类切换图片,因为popwindowactivity中,要改变fragment的值;fragment只能向外公布接口了changeOtherBeautyType方法用来改变图片类型并在此方法中做刷新操作

 

运行项目检验成果

               

OMG的图片滑动明显混乱不堪,popwindow说好的从Toolbar下方滑动显示呢?这都是什么呢?

 

遭受打击之后,博主孕育出了第二篇Android学习之优化美女图片浏览器,让我们一同来优化吧;相信这个过程是痛并快乐着的,哈哈...欲知详情且听下回分解

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值