Android 高仿微信朋友圈动态支持双击手势放大并滑动查看图片效果

最近参与了开发一款旅行APP,其中包含实时聊天和动态评论功能,终于耗时几个月几个伙伴完成了,今天就小结一下至于实时聊天功能如果用户不多的情况可以scoket实现,如果用户万级就可以采用开源的smack + opnefile实现,也可以用mina开源+XMMP,至于怎么搭建和实现,估计目前github上一搜一大把,至于即时通讯怕误人子弟,暂且不做介绍,现就把实现的一个微信朋友圈的小功能介绍一下。

先上效果图:

一拿到主流的UI需求,大致分析下,需要我ListView嵌套Gridview,而gridView的行数也和图片总数有关系,因此通过个数我们可以动态设置条目的宽高,而点击图片放大我们可一跳转到另一界面,图片左右滑动可以用viewpager实现,双击图片放大和手指缩放图片也可以用就监听手势进行不断放大,对于安卓事件不熟悉的朋友可以直接使用一个著名的photoVIew开源项目,支持手势缩放图片和滑动图片实现画廊功能,也很好的解决了内存溢出问题。

  一 配置ImageLoader      

本项目中加载网络图片我就直接使用imageLoader,但建议还是去看下源码,因为开源项目本身自带缓存机制,有很好的缓存技巧,有很多东西值得我们借鉴。其不仅可以加载本地图片(文件path),也支持加载网络图片(url),并且自带防止内存溢出功能。

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)//缓存一百张图片
        .writeDebugLogs()
        .build();
    ImageLoader.getInstance().init(config);
  }
} 

 二 准备主界面和需要的基础类

  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);
    //加载网络图片
    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,为了演示这里使用常量,一般从数据库中或网络中获取
    intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_URLS, urls);
    intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_INDEX, position);
    mContext.startActivity(intent);
  }
  // 优化listview
  private static class ViewHolder {
    public TextView name;
    public ImageView avator;
    TextView content;
    NoScrollGridView gridView;
  }
}

  2  主界面

实际项目中数据是数据是从服务器获取的,本次就只将图片从网络获取,

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() {
    // 模拟网络获取数据
    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

因为Listview的条目中包含Gridview,在这里还需要为它创建atapter

由于adapter没太多技术含量,因此重点部分列出,在这里我们需要判断下适配的数据眼总数,微信最大数是9张,显示一张的时候,图片比较大,两张的时候稍微减少,四张的时候两列两行和两张的大小一致,其他张数的时候都是三行三列的九宫格。

@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 );
  }
} 

三 点击图片后的基础类

   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);
    // 更新下标
    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 = "下载错误";
          break;
        case DECODING_ERROR:
          message = "图片无法显示";
          break;
        case NETWORK_DENIED:
          message = "网络有问题,无法下载";
          break;
        case OUT_OF_MEMORY:
          message = "图片太大无法显示";
          break;
        case UNKNOWN:
          message = "未知的错误";
          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整体功能较复杂,因此使用第三方的东西,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加入到工程中,

总结一下实现以上功能我们使用了第三的imagloader,支持手势缩放的PhotoView,圆形图像的circleimageView,熟悉安卓view绘制机制加载过程,事件传递和分发的朋友是不需要第三方开源项目的支持的,但是对于入门不久的同学,学会怎样使用开源框架就可以,但是想要提高开源项目的的核心还是需要了解的,欢迎阅读

运行效果图:

有兴趣的朋友建议阅读下:

安卓事件机制(一)和上篇关于View的博文。谢谢交流和分享。

demo源码下载地址:https://github.com/Tamicer/CHatMomentDemo

以上所述是小编给大家介绍的Android 高仿微信朋友圈动态支持双击手势放大并滑动查看图片效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android仿微信列表滑动删除之可滑动控件(一)

    这次是列表滑动删除的第三波,仿微信的列表滑动删除.先上个效果图: 前面的文章里面说过开源框架SwipeListView的实现原理是每个列表item中包含上下两层view,普通状态下上层的view覆盖着下层的view,当用户滑开上层的view,下层的view就显示出来了.但是仔细观察微信列表的item,很明显并非这个实现方案,微信的item应该一个单层view,只不过这个item超出了所在的ListView的宽度,在用户滑动item的时候,item超出屏幕的view则会显示在屏幕之上,这种滑动实现

  • Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能

    如何为不同的list item呈现不同的菜单,本文实例就为大家介绍了Android仿微信或QQ滑动弹出编辑.删除菜单效果.增加下拉刷新等功能的实现,分享给大家供大家参考,具体内容如下 效果图: 1. 下载开源项目,并将其中的liberary导入到自己的项目中: 2. 使用SwipeMenuListView代替ListView,在页面中布局: <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefresh

  • Android实现仿微信tab高亮icon粘着手的滑动效果

    微信的主页分为3个tab,被选中的tab的tabwidget下面会有一个高亮的长条icon,而当切换tab页面的时候,这个icon不是等到tab切换完成后再出现在当前被选中的tab的tabwidget的下面,而是会随着viewpager滑动页面的动作也进行滑动,从前一个tabwidget滑到当前被选中的tabwidget,像viewpager一样有一种粘着你的手的效果,体验很赞.上个图: 本篇分析如何实现这个效果. 首先基本知识是,实现不同tab页之间可以滑动切换需要用到TabPageIndic

  • Android仿微信右滑返回功能的实例代码

    先上效果图,如下: 先分析一下功能的主要技术点,右滑即手势判断,当滑到一直距离时才执行返回,并且手指按下的位置是在屏幕的最左边(这个也是有一定范围的),  这些可以实现onTouchEvent来实现. 接着就是返回时,有滑动效果,很显然这个是Acitivty切换动画实现的.好啦,分析完了就开干.下面上代码: @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case Mot

  • Android高仿微信对话列表滑动删除效果

    前言 用过微信的都知道,微信对话列表滑动删除效果是很不错的,这个效果我们也可以有.思路其实很简单,弄个ListView,然后里面的每个item做成一个可以滑动的自定义控件即可.由于ListView是上下滑动而item是左右滑动,因此会有滑动冲突,也许你需要了解下android中点击事件的派发流程,请参考Android源码分析-点击事件派发机制.我的解决思路是这样的:重写ListView的onInterceptTouchEvent方法,在move的时候做判断,如果是左右滑动就返回false,否则返

  • Android实现微信首页左右滑动切换效果

    大家看到微信首页切换效果有没有觉得很炫,滑动切换,点击底部bar瞬间切换,滑动切换渐变效果,线上效果图: 之前也在博客上看到别人的实现,再次基础上,我做了些优化.首先说下实现原理,大神略过,o(╯□╰)o 页面上看到的三个页面是三个Fragment, 左右滑动使用viewpager,相信大家也都是这么再用,那么底部用的是什么技术呢,底部渐变其实就是重写了ImageView,以及在左右滑动时,改变了TextView的颜色值,是不是很简单...下面我们一步一步的来: 1.自定义ImageView:

  • Android仿微信列表滑动删除 如何实现滑动列表SwipeListView

    接上一篇,本篇主要讲如何实现滑动列表SwipeListView. 上篇完成了滑动控件SwipeItemView,这个控件是一个自定义的ViewGroup,作为列表的一个item,为列表提供一些方法让这个SwipeItemView能滑动其视图内容,同时滑动过程中会有顺滑的动画效果.而本篇讲的SwipeListView则是这个列表的具体实现了.当然啦,这个SwipeListView继承自ListView,为了实现我们需要的功能,重点就是重写ListView的onTouchEvent()以及onInt

  • Android 高仿微信朋友圈动态支持双击手势放大并滑动查看图片效果

    最近参与了开发一款旅行APP,其中包含实时聊天和动态评论功能,终于耗时几个月几个伙伴完成了,今天就小结一下至于实时聊天功能如果用户不多的情况可以scoket实现,如果用户万级就可以采用开源的smack + opnefile实现,也可以用mina开源+XMMP,至于怎么搭建和实现,估计目前github上一搜一大把,至于即时通讯怕误人子弟,暂且不做介绍,现就把实现的一个微信朋友圈的小功能介绍一下. 先上效果图: 一拿到主流的UI需求,大致分析下,需要我ListView嵌套Gridview,而grid

  • Android 高仿微信朋友圈拍照上传功能

    模仿微信朋友圈发布动态,输入文字支持文字多少高度自增,有一个最小输入框高度,输入文字有限制,不过这些都很easy! 1. PhotoPicker的使用 这是一个支持选择多张图片,点击图片放大,图片之间左右滑动互相切换的库,同时支持图片删除的库,效果类似微信. (1) 添加PhotoPicker的架包 (2) 使用 选择图片:安卓6.0以后需要在代码中添加读写sd卡和相机的权限 当然清单文件中也需要添加的 PhotoPicker.builder() .setPhotoCount(maxPhoto)

  • Android自定义SwipeRefreshLayout高仿微信朋友圈下拉刷新

    上一篇文章里把SwipeRefreshLayout的原理简单过了一下,大致了解了其工作原理,不熟悉的可以去看一下:http://www.jb51.net/article/89310.htm 上一篇里最后提到,SwipeRefreshLayout的可定制性是比较差的,看源码会发现跟样式相关的几个类都是private的而且方法是写死的,只暴露出了几个颜色设置的方法.这样使得SwipeRefreshLayout的使用比较简单,主要就是设置一个监听器在onRefresh方法里完成刷新逻辑.讲道理Swip

  • Android GridView仿微信朋友圈显示图片

    最近项目要求上传多图并且多图显示,而且要规则的显示,就像微信朋友圈的图片显示一样. 利用GridView再适合不过了,GridView可以动态加载图片的数量,而且还比较规律,下面说一下自己的思路: 1.获取网络图片 2.初始化gridview,自定义适配器 3.根据图片数量设置gridview的列数 4.更新适配器 下面贴上部分源码并给大家解析一下 一.首先是GridView的item <com.view.SquareLayout xmlns:android="http://schemas

  • Android仿微信朋友圈全文、收起功能的实例代码

    前言 一般在社交APP中都有类似朋友圈的功能,其中发表的动态内容很长的时候不可能让它全部显示.这里就需要做一个仿微信朋友圈全文.收起功能来解决该问题.在网上看到一个例子-->http://www.jb51.net/article/105251.htm,写的很不错,但是有个bug,他这个Demo只有在条目固定的时候才正常,当增加.删除条目的时候会出现全文.收起显示混乱的问题.原因是他使用了固定的position作为key来保存当前显示的状态.这篇文章在他的基础上进行优化. 效果图 具体代码 (详细

  • Android 仿微信朋友圈点赞和评论弹出框功能

    贡献/下载源码:https://github.com/mmlovesyy/PopupWindowDemo 本文简单模仿微信朋友圈的点赞和评论弹出框,布局等细节请忽略,着重实现弹出框.发评论,及弹出位置的控制. 1. 微信弹出框 微信朋友圈的点赞和评论功能,有2个组成部分: 点击左下角的"更多"按钮,弹出对话框: 点击评论,弹出输入框,添加评论并在页面中实时显示: 微信朋友圈点赞和评论功能 2. 实际效果 本文将建一个 ListView,在其 Item 中简单模仿微信的布局,然后着重实现

  • Android自定义TextView仿微信朋友圈文字展开全文功能

    Android自定义TextView仿微信朋友圈文字信息,展开全文功能 代码及注释如下: 首先写一个xml文件 showmore.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical

  • Android高仿微信表情输入与键盘输入详解

    最近公司在项目上要使用到表情与键盘的切换输入,自己实现了一个,还是存在些缺陷,比如说键盘与表情切换时出现跳闪问题,这个相当困扰我,不过所幸在Github(其中一个不错的开源项目,其代码整体结构很不错)并且在论坛上找些解决方案,再加上我也是研究了好多个开源项目的代码,最后才苦逼地整合出比较不错的实现效果,可以说跟微信基本一样(嘿嘿,只能说目前还没发现大Bug,若发现大家一起日后慢慢完善,这里我也只是给出了实现方案,拓展其他表情我并没有实现哈,不过代码中我实现了一个可拓展的fragment模板以便大

  • Android 高仿微信语音聊天页面高斯模糊(毛玻璃效果)

    目前的应用市场上,使用毛玻璃效果的APP随处可见,比如用过微信语音聊天的人可以发现,语音聊天页面就使用了高斯模糊效果. 先看下效果图: 仔细观察上图,我们可以发现,背景图以用户头像为模板,对其进行了高斯模糊,并把它作为整个页面的背景色. 关于Android如何快速实现高斯模糊(毛玻璃效果),网上一堆相关介绍,可参考下面文章一种快速毛玻璃虚化效果实现–Android. 下面直接给出模糊化工具类(已验证可行): import android.graphics.Bitmap; /** * 快速模糊化工

  • Android 高仿微信支付数字键盘功能

    现在很多app的支付.输入密码功能,都已经开始使用自定义数字键盘,不仅更加方便.其效果着实精致. 下面带着大家学习下,如何高仿微信的数字键盘,可以拿来直接用在自身的项目中. 先看下效果图: 1. 自定义布局 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

随机推荐