Android 高仿微信朋友圈動(dòng)態(tài)支持雙擊手勢(shì)放大并滑動(dòng)查看圖片效果
最近參與了開發(fā)一款旅行APP,其中包含實(shí)時(shí)聊天和動(dòng)態(tài)評(píng)論功能,終于耗時(shí)幾個(gè)月幾個(gè)伙伴完成了,今天就小結(jié)一下至于實(shí)時(shí)聊天功能如果用戶不多的情況可以scoket實(shí)現(xiàn),如果用戶萬級(jí)就可以采用開源的smack + opnefile實(shí)現(xiàn),也可以用mina開源+XMMP,至于怎么搭建和實(shí)現(xiàn),估計(jì)目前github上一搜一大把,至于即時(shí)通訊怕誤人子弟,暫且不做介紹,現(xiàn)就把實(shí)現(xiàn)的一個(gè)微信朋友圈的小功能介紹一下。
先上效果圖:

一拿到主流的UI需求,大致分析下,需要我ListView嵌套Gridview,而gridView的行數(shù)也和圖片總數(shù)有關(guān)系,因此通過個(gè)數(shù)我們可以動(dòng)態(tài)設(shè)置條目的寬高,而點(diǎn)擊圖片放大我們可一跳轉(zhuǎn)到另一界面,圖片左右滑動(dòng)可以用viewpager實(shí)現(xiàn),雙擊圖片放大和手指縮放圖片也可以用就監(jiān)聽手勢(shì)進(jìn)行不斷放大,對(duì)于安卓事件不熟悉的朋友可以直接使用一個(gè)著名的photoVIew開源項(xiàng)目,支持手勢(shì)縮放圖片和滑動(dòng)圖片實(shí)現(xiàn)畫廊功能,也很好的解決了內(nèi)存溢出問題。
一 配置ImageLoader
本項(xiàng)目中加載網(wǎng)絡(luò)圖片我就直接使用imageLoader,但建議還是去看下源碼,因?yàn)殚_源項(xiàng)目本身自帶緩存機(jī)制,有很好的緩存技巧,有很多東西值得我們借鑒。其不僅可以加載本地圖片(文件path),也支持加載網(wǎng)絡(luò)圖片(url),并且自帶防止內(nèi)存溢出功能。
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
DisplayImageOptions defaultOptions = new DisplayImageOptions
.Builder()
.showImageForEmptyUri(R.drawable.empty_photo)
.showImageOnFail(R.drawable.empty_photo)
.cacheInMemory(true)
.cacheOnDisc(true)
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration
.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultOptions)
.discCacheSize(50 * 1024 * 1024)//
.discCacheFileCount(100)//緩存一百?gòu)垐D片
.writeDebugLogs()
.build();
ImageLoader.getInstance().init(config);
}
}
二 準(zhǔn)備主界面和需要的基礎(chǔ)類
1 Listadapter
public class FridListAdapter extends BaseAdapter{
private ArrayList<MyBean> mList;
private LayoutInflater mInflater;
private Context mContext;
public FridListAdapter(Context context,ArrayList<MyBean> list) {
mInflater = LayoutInflater.from(context);
mContext=context;
this.mList=list;
}
@Override
public int getCount() {
return mList==null?0:mList.size();
}
@Override
public MyBean getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return getItem(position).id;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null);
holder.avator=(ImageView)convertView.findViewById(R.id.avator);
holder.name=(TextView)convertView.findViewById(R.id.name);
holder.content = (TextView) convertView.findViewById(R.id.content);
holder.gridView=(NoScrollGridView)convertView.findViewById(R.id.gridView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final MyBean bean = getItem(position);
//加載網(wǎng)絡(luò)圖片
ImageLoader.getInstance().displayImage(bean.avator, holder.avator);
holder.name.setText(bean.name);
holder.content.setText(bean.content);
if(bean.urls!=null&&bean.urls.length>0){
holder.gridView.setVisibility(View.VISIBLE);
holder.gridView.setAdapter(new DynamicGridAdapter(bean.urls, mContext));
holder.gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
imageBrower(position,bean.urls);
}
});
}else{
holder.gridView.setVisibility(View.GONE);
}
return convertView;
}
private void imageBrower(int position, String[] urls) {
Intent intent = new Intent(mContext, ImagePagerActivity.class);
// 圖片url,為了演示這里使用常量,一般從數(shù)據(jù)庫(kù)中或網(wǎng)絡(luò)中獲取
intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_URLS, urls);
intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_INDEX, position);
mContext.startActivity(intent);
}
// 優(yōu)化listview
private static class ViewHolder {
public TextView name;
public ImageView avator;
TextView content;
NoScrollGridView gridView;
}
}
2 主界面
實(shí)際項(xiàng)目中數(shù)據(jù)是數(shù)據(jù)是從服務(wù)器獲取的,本次就只將圖片從網(wǎng)絡(luò)獲取,
public class MainActivity extends ListActivity {
public static final String TAG = "MainActivity";
private FridListAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new LoderDataTask().execute();
}
class LoderDataTask extends AsyncTask<Void, Void, MessageModle> {
@Override
protected MessageModle doInBackground(Void... params) {
Gson gson = new Gson();
MessageModle msg = gson.fromJson(getData(), MessageModle.class);
return msg;
}
@Override
protected void onPostExecute(MessageModle result) {
mAdapter = new FridListAdapter(MainActivity.this, result.list);
setListAdapter(mAdapter);
}
}
private String getData() {
// 模擬網(wǎng)絡(luò)獲取數(shù)據(jù)
String json = "{\"code\":200,\"msg\":\"ok\",list:["
+ "{\"id\":110,\"avator\":\"http://img0.bdstatic.com/img/image/shouye/leimu/mingxing.jpg\",\"name\":\"趙薇\",\"content\":\"今天不開心!\",\"urls\":[]},"
+ "{\"id\":111,\"avator\":\"http://image.cnwest.com/attachement/jpg/site1/20110507/001372d8a36f0f2f4c953a.jpg\",\"name\":\"李晨\",\"content\":\"我們\","
+ " \"urls\":[\"http://guangdong.sinaimg.cn/2015/0530/U11307P693DT20150530094310.jpg\"]},"
+ "{\"id\":114,\"avator\":\"http://img.hexun.com/2009-05-01/117287830.jpg\",\"name\":\"小馬哥\",\"content\":\"今天淘寶了嗎\",\"urls\":["
+ "\"http://g.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=ccd33b46d53f8794d7ff4b26e2207fc9/0d338744ebf81a4c0f993437d62a6059242da6a1.jpg\","
+ "\"http://f.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=6b62f61bac6eddc422e7b7f309e0c7c0/6159252dd42a2834510deef55ab5c9ea14cebfa1.jpg\","
+ "\"http://g.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=e58fb67bc8ea15ce45eee301863b4bce/a5c27d1ed21b0ef4fd6140a0dcc451da80cb3e47.jpg\","
+ "\"http://c.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=cdab1512d000baa1be2c44b3772bc82f/91529822720e0cf3855c96050b46f21fbf09aaa1.jpg\"]},"
+ "{\"id\":112,\"avator\":\"http://img3.yxlady.com/yl/UploadFiles_5361/20150528/20150528050208705.jpg\",\"name\":\"鄧超\",\"content\":\"奔跑吧兄弟! 歡迎收看!\",\"urls\":[\"http://upload.cbg.cn/2015/0305/1425518659246.jpg\","
+ "\"http://www.people.com.cn/mediafile/pic/20150619/30/4179219540177204330.jpg\"]},"
+ "{\"id\":113,\"avator\":\"http://img4.imgtn.bdimg.com/it/u=945108765,1070109457&fm=21&gp=0.jpg\",\"name\":\"奧巴馬\",\"content\":\"holle\",\"urls\":[\"http://f.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=6b62f61bac6eddc422e7b7f309e0c7c0/6159252dd42a2834510deef55ab5c9ea14cebfa1.jpg\",\"http://g.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=e58fb67bc8ea15ce45eee301863b4bce/a5c27d1ed21b0ef4fd6140a0dcc451da80cb3e47.jpg\",\"http://c.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=cdab1512d000baa1be2c44b3772bc82f/91529822720e0cf3855c96050b46f21fbf09aaa1.jpg\"]}]}";
return json;
}
3 GridView的Adapter
因?yàn)長(zhǎng)istview的條目中包含Gridview,在這里還需要為它創(chuàng)建atapter
由于adapter沒太多技術(shù)含量,因此重點(diǎn)部分列出,在這里我們需要判斷下適配的數(shù)據(jù)眼總數(shù),微信最大數(shù)是9張,顯示一張的時(shí)候,圖片比較大,兩張的時(shí)候稍微減少,四張的時(shí)候兩列兩行和兩張的大小一致,其他張數(shù)的時(shí)候都是三行三列的九宮格。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyGridViewHolder viewHolder;
if (convertView == null) {
viewHolder = new MyGridViewHolder();
convertView = mLayoutInflater.inflate(R.layout.gridview_item,
parent, false);
viewHolder.imageView = (ImageView) convertView
.findViewById(R.id.album_image);
convertView.setTag(viewHolder);
} else {
viewHolder = (MyGridViewHolder) convertView.getTag();
}
String url = getItem(position);
if (getCount() == 1) {
viewHolder.imageView.setLayoutParams(new android.widget.AbsListView.LayoutParams(300, 250));
}
if (getCount() == 2 ||getCount() == 4) {
viewHolder.imageView.setLayoutParams(new android.widget.AbsListView.LayoutParams(200, 200));
}
ImageLoader.getInstance().displayImage(url, viewHolder.imageView);
return convertView;
}
4 新建用于支持九宮格自定義的Gridview
public class NoScrollGridView extends GridView {
public NoScrollGridView(Context context) {
super(context);
}
public NoScrollGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = 0;
int size = getAdapter().getCount();
if (size == 1) {
setNumColumns(1);
}
if ( size==2 || size == 4 ) {
setNumColumns(2);
}
else {
setNumColumns(3);
}
expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec,expandSpec );
}
}
三 點(diǎn)擊圖片后的基礎(chǔ)類
1 建立大圖查看器viewpaer
public class ImagePagerActivity extends FragmentActivity {
private static final String STATE_POSITION = "STATE_POSITION";
public static final String EXTRA_IMAGE_INDEX = "image_index";
public static final String EXTRA_IMAGE_URLS = "image_urls";
private HackyViewPager mPager;
private int pagerPosition;
private TextView indicator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_detail_pager);
pagerPosition = getIntent().getIntExtra(EXTRA_IMAGE_INDEX, 0);
String[] urls = getIntent().getStringArrayExtra(EXTRA_IMAGE_URLS);
mPager = (HackyViewPager) findViewById(R.id.pager);
ImagePagerAdapter mAdapter = new ImagePagerAdapter(
getSupportFragmentManager(), urls);
mPager.setAdapter(mAdapter);
indicator = (TextView) findViewById(R.id.indicator);
CharSequence text = getString(R.string.viewpager_indicator, 1, mPager
.getAdapter().getCount());
indicator.setText(text);
// 更新下標(biāo)
mPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int arg0) {
CharSequence text = getString(R.string.viewpager_indicator,
arg0 + 1, mPager.getAdapter().getCount());
indicator.setText(text);
}
});
if (savedInstanceState != null) {
pagerPosition = savedInstanceState.getInt(STATE_POSITION);
}
mPager.setCurrentItem(pagerPosition);
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_POSITION, mPager.getCurrentItem());
}
private class ImagePagerAdapter extends FragmentStatePagerAdapter {
public String[] fileList;
public ImagePagerAdapter(FragmentManager fm, String[] fileList) {
super(fm);
this.fileList = fileList;
}
@Override
public int getCount() {
return fileList == null ? 0 : fileList.length;
}
@Override
public Fragment getItem(int position) {
String url = fileList[position];
return ImageDetailFragment.newInstance(url);
}
}
2 查看大圖界面
public class ImageDetailFragment extends Fragment {
private String mImageUrl;
private ImageView mImageView;
private ProgressBar progressBar;
private PhotoViewAttacher mAttacher;
public static ImageDetailFragment newInstance(String imageUrl) {
final ImageDetailFragment f = new ImageDetailFragment();
final Bundle args = new Bundle();
args.putString("url", imageUrl);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mImageUrl = getArguments() != null ? getArguments().getString("url") : null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
mImageView = (ImageView) v.findViewById(R.id.image);
mAttacher = new PhotoViewAttacher(mImageView);
mAttacher.setOnPhotoTapListener(new OnPhotoTapListener() {
@Override
public void onPhotoTap(View arg0, float arg1, float arg2) {
getActivity().finish();
}
});
progressBar = (ProgressBar) v.findViewById(R.id.loading);
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ImageLoader.getInstance().displayImage(mImageUrl, mImageView, new SimpleImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
String message = null;
switch (failReason.getType()) {
case IO_ERROR:
message = "下載錯(cuò)誤";
break;
case DECODING_ERROR:
message = "圖片無法顯示";
break;
case NETWORK_DENIED:
message = "網(wǎng)絡(luò)有問題,無法下載";
break;
case OUT_OF_MEMORY:
message = "圖片太大無法顯示";
break;
case UNKNOWN:
message = "未知的錯(cuò)誤";
break;
}
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
progressBar.setVisibility(View.GONE);
mAttacher.update();
}
});
}
四 界面的頭像圓形
圓形頭像用主流的circleimageview.jar的框架,但是有興趣的朋友也可以自定義Imagview采用重寫onDrawI()畫圓形的方式將bitmap畫上去,由于此demo整體功能較復(fù)雜,因此使用第三方的東西,ListView條目布局如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp" >
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avator"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/empty_photo" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/avator"
android:textColor="#576B95"
android:textSize="16sp"
android:text="name" />
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/name"
android:layout_marginLeft="10dp"
android:textSize="12sp"
android:layout_toRightOf="@id/avator"
android:text="content" />
<com.loveplusplus.demo.image.NoScrollGridView
android:id="@+id/gridView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:layout_below="@id/content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/avator"
android:horizontalSpacing="1dp"
android:numColumns="3"
android:visibility="gone"
android:verticalSpacing="1dp" />
</RelativeLayout>
接下來我們還需要將主流的photoView.jar加入到工程中,
總結(jié)一下實(shí)現(xiàn)以上功能我們使用了第三的imagloader,支持手勢(shì)縮放的PhotoView,圓形圖像的circleimageView,熟悉安卓view繪制機(jī)制加載過程,事件傳遞和分發(fā)的朋友是不需要第三方開源項(xiàng)目的支持的,但是對(duì)于入門不久的同學(xué),學(xué)會(huì)怎樣使用開源框架就可以,但是想要提高開源項(xiàng)目的的核心還是需要了解的,歡迎閱讀
運(yùn)行效果圖:
有興趣的朋友建議閱讀下:
安卓事件機(jī)制(一)和上篇關(guān)于View的博文。謝謝交流和分享。
demo源碼下載地址:https://github.com/Tamicer/CHatMomentDemo
以上所述是小編給大家介紹的Android 高仿微信朋友圈動(dòng)態(tài)支持雙擊手勢(shì)放大并滑動(dòng)查看圖片效果,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Android基于IJKPlayer視頻播放器簡(jiǎn)單封裝設(shè)計(jì)
這篇文章主要介紹了Android基于IJKPlayer視頻播放器簡(jiǎn)單封裝設(shè)計(jì),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
Android利用滑動(dòng)菜單框架實(shí)現(xiàn)滑動(dòng)菜單效果
這篇文章主要介紹了Android實(shí)現(xiàn)滑動(dòng)菜單特效之滑動(dòng)菜單框架完全解析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Android常用三方庫(kù)混淆規(guī)則整理(小結(jié))
這篇文章主要介紹了Android常用三方庫(kù)混淆規(guī)則整理(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07
Android從網(wǎng)絡(luò)中獲得一張圖片并顯示在屏幕上的實(shí)例詳解
這篇文章主要介紹了Android從網(wǎng)絡(luò)中獲得一張圖片并顯示在屏幕上的實(shí)例詳解的相關(guān)資料,希望通過本文能幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08
Android使用popupWindow仿微信彈出框使用方法
這篇文章主要為大家詳細(xì)介紹了Android使用popupWindow仿微信彈出框使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09
詳解如何在Android Studio中添加RecyclerView-v7支持包
本篇文章主要介紹了詳解如何在Android Studio中添加RecyclerView-v7支持包,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Android跳轉(zhuǎn)三方應(yīng)用實(shí)例代碼
大家好,本篇文章主要講的是Android跳轉(zhuǎn)三方應(yīng)用實(shí)例代碼,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12

