Android 高仿微信发朋友圈浏览图片效果
最后更新于:2022-04-01 15:54:53
最近一直在高仿微信、高仿微信,今天小编再给大家分享一个仿微信发朋友圈浏览图片的效果....
好了,先看一下效果吧:
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed848f8a.jpg "")
下面就来说一下具体怎么实现的:
###实现思路
1.首先我们要获取数据源,数据源就是我们的每条说说(包括姓名、标题、图片数组)
2.自定义适配器(ListView嵌套着GridView)
3.图片点击浏览图片(Fragment+ViewPager)
###具体实现
1.初始化数据源,设置适配器,看一下代码:
~~~
public class MyActivity extends Activity {
/*图片显示列表*/
private ListView listView;
/*图片URL数组*/
private List<ContentBean> contentBeans;
/*说说适配器*/
private MyAdapter adapter;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initData();
initViews();
}
/**
* 初始化数据
* by Hankkin at:2015-11-22 23:21:28
*/
private void initData(){
contentBeans = new ArrayList<ContentBean>();
ArrayList<String> imgUrls1 = new ArrayList<String>();
imgUrls1.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
ContentBean cb1 = new ContentBean(1,"java","Sun Microsystems",imgUrls1);
contentBeans.add(cb1);
ArrayList<String> imgUrls2 = new ArrayList<String>();
imgUrls2.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls2.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls2.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
ContentBean cb2 = new ContentBean(2,"OC","Stepstone",imgUrls2);
contentBeans.add(cb2);
ArrayList<String> imgUrls3 = new ArrayList<String>();
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
imgUrls3.add("http://7xojuc.com1.z0.glb.clouddn.com/110H2E40-6.jpg?attname=&e=1448288962&token=KDsCqUAWz3P-YT6In6oPnT56Eh2cig4zgQd12SJ_:AlTjfYD9SBFOdB4jmmZuKXAMOp8");
ContentBean cb3 = new ContentBean(3,"python","Guido van Rossum",imgUrls3);
contentBeans.add(cb3);
}
/**
* 初始化组件
* by Hankkin at:2015-11-22 23:21:44
*/
private void initViews(){
listView = (ListView) findViewById(R.id.lv_my);
adapter = new MyAdapter(MyActivity.this,contentBeans);
listView.setAdapter(adapter);
}
}
~~~
这里面的图片是我上传到七牛的网络图片,加载图片是用ImageLoader,下面也有具体的ImageLoader配置。
2.看一下适配器内容
在说说列表适配器中去设置图片的适配器,图片的GridView是重写了一个不能滑动的GridView,重写一下OnMeasure();
~~~
public class MyAdapter extends BaseAdapter {
private Context context;
private List<ContentBean> data;
public MyAdapter(Context context, List<ContentBean> data) {
this.context = context;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int i) {
return data.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = View.inflate(context, R.layout.item, null);
holder.gridView = (NoScrollGridView) view.findViewById(R.id.gridview);
holder.tvName = (TextView) view.findViewById(R.id.tv_name);
holder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
final ContentBean bean = data.get(i);
holder.tvName.setText(bean.getName());
holder.tvTitle.setText(bean.getTitle());
if (data != null && data.size() > 0) {
holder.gridView.setAdapter(new ImageGridAdapter(context, bean.getImgUrls()));
}
/**
* 图片列表点击事件
*/
holder.gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(context, ImagePagerActivity.class);
intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_URLS, (Serializable) bean.getImgUrls());
intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_INDEX, i);
context.startActivity(intent);
}
});
return view;
}
class ViewHolder {
TextView tvName, tvTitle;
NoScrollGridView gridView;
}
}
~~~
3.然后就是图片浏览,这个网上也有好多的Demo,也有详细的讲解,直接拽过来用就可以了,下面的图片数量是监听setOnPageChangeListener()来改变下面的图片索引值
~~~
/**
* 图片查看器
* Created by Hankkin on 15/11/22.
*/
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);
ArrayList<String> urls = getIntent().getStringArrayListExtra(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 ArrayList<String> fileList;
public ImagePagerAdapter(FragmentManager fm, ArrayList<String> fileList) {
super(fm);
this.fileList = fileList;
}
@Override
public int getCount() {
return fileList == null ? 0 : fileList.size();
}
@Override
public Fragment getItem(int position) {
String url = fileList.get(position);
return ImageDetailFragment.newInstance(url);
}
}
}
~~~
图片Fragment的详细界面,里面有长按点击事件,和图片加载的状态
~~~
package com.hankkin.WeiXinLookImgsDemo.activty;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.hankkin.WeiXinLookImgsDemo.R;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.others.PhotoViewAttacher;
/**
* 单张图片显示Fragment
* Created by Hankkin on 15/11/22.
*/
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 PhotoViewAttacher.OnPhotoTapListener() {
@Override
public void onPhotoTap(View arg0, float arg1, float arg2) {
getActivity().finish();
}
});
mAttacher.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
Toast.makeText(getActivity().getApplicationContext(),"保存",Toast.LENGTH_SHORT).show();
return false;
}
});
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();
}
});
}
}
~~~
忘了ImageLoader的初始化工作了,给大家加上吧,我写到Application里了
~~~
private MyApplication context;
@Override
public void onCreate() {
super.onCreate();
context = this;
initImageLoader(context);
}
/**
* 初始化Imageloader
* by Hankkin at:2015-11-22 23:20:29
* @param context
*/
public static void initImageLoader(Context context){
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_launcher)
.showImageOnFail(R.drawable.ic_launcher)
.resetViewBeforeLoading(false) // default
.delayBeforeLoading(0)
.cacheInMemory(true) // default
.cacheOnDisk(true) // default
.considerExifParams(true) // default
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
.bitmapConfig(Bitmap.Config.ARGB_8888) // default
.displayer(new SimpleBitmapDisplayer()) // default
.handler(new Handler()) // default
.build();
File picPath = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "weixinlookimgdemo" + File.separator + "files");
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions
.diskCacheExtraOptions(480, 800, null)
.threadPoolSize(3) // default
.threadPriority(Thread.NORM_PRIORITY - 1) // default
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024))
.memoryCacheSize(2 * 1024 * 1024)
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiscCache(picPath)) // default
.diskCacheSize(50 * 1024 * 1024)
.diskCacheFileCount(1000)
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
.imageDownloader(new BaseImageDownloader(context)) // default
.imageDecoder(new BaseImageDecoder(true)) // default
.defaultDisplayImageOptions(options) // default
.writeDebugLogs()
.build();
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config);
}
~~~
这个效果就很容易实现了,揣摩别人的APP很容易,但是我们要试着去实现别人的APP,生命在于折腾,折腾吧,亲们…..
最后小编把源码上传到了Github上,宝贝们,star吧
[https://github.com/Hankkin/WeiXinLookImgsDemo](https://github.com/Hankkin/WeiXinLookImgsDemo)
Android 高仿微信群聊头像
最后更新于:2022-04-01 15:54:51
最近小编搞了一个仿微信群聊头像的一个功能,分享给大家...
工作中需要实现仿钉钉群头像的一个功能,就是个人的头像拼到一起显示,看了一下市场上的APP好像微信的群聊头像是组合的,QQ的头像不是,别的好像也没有了。今天给大家分享一下怎么实现的吧。首先我们先看一下效果图:
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed7e8c6b.jpg "")
好了,下面说一下具体怎么实现的:
### 实现思路
1.首先获取Bitmap图片(本地、网络)
2.创建一个指定大小的缩略图
3.组合Bitmap图片
很简单,本地图片需要我们从本地读取,如果是网络图片我们也可以根据URL来获取bitmap进行组合
### 具体实现过程
1.布局文件:
~~~
<LinearLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:gravity="center"
android:orientation="vertical"
android:background="#987"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<TextView
android:background="#fff"
android:layout_width="match_parent"
android:layout_height="1dp"/>
<ImageView
android:src="@drawable/j"
android:id="@+id/iv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:background="#fff"
android:layout_width="match_parent"
android:layout_height="1dp"/>
<ImageView
android:src="@drawable/j"
android:id="@+id/iv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:background="#fff"
android:layout_width="match_parent"
android:layout_height="1dp"/>
<ImageView
android:src="@drawable/j"
android:id="@+id/iv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:background="#fff"
android:layout_width="match_parent"
android:layout_height="1dp"/>
<ImageView
android:src="@drawable/j"
android:id="@+id/iv4"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:background="#fff"
android:layout_width="match_parent"
android:layout_height="1dp"/>
</LinearLayout>
~~~
四个ImageView控件,用来显示图片不说了
2.获取Bitmap,设定图片的属性
~~~
/**
* 获取图片数组实体
* by Hankkin at:2015-11-19 22:00:55
* @param count
* @return
*/
private List<BitmapBean> getBitmapEntitys(int count) {
List<BitmapBean> mList = new ArrayList<>();
String value = PropertiesUtil.readData(this, String.valueOf(count),
R.raw.data);
String[] arr1 = value.split(";");
int length = arr1.length;
for (int i = 0; i < length; i++) {
String content = arr1[i];
String[] arr2 = content.split(",");
BitmapBean entity = null;
for (int j = 0; j < arr2.length; j++) {
entity = new BitmapBean();
entity.setX(Float.valueOf(arr2[0]));
entity.setY(Float.valueOf(arr2[1]));
entity.setWidth(Float.valueOf(arr2[2]));
entity.setHeight(Float.valueOf(arr2[3]));
}
mList.add(entity);
}
return mList;
}
~~~
3.创建压缩图片,这里我们用到了ThumbnailUtils中的extractThumbnail()方法,参数为bitmap,width,height
~~~
/**
* 初始化数据
* by Hankkin at:2015-11-19 21:59:03
*/
private void initData(){
/*获取四个图片数组*/
bitmapBeans1 = getBitmapEntitys(1);
bitmapBeans2 = getBitmapEntitys(2);
bitmapBeans3 = getBitmapEntitys(3);
bitmapBeans4 = getBitmapEntitys(4);
/*bitmap缩略图*/
Bitmap[] bitmaps1 = {
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans1
.get(0).getWidth(), (int) bitmapBeans1.get(0).getWidth())};
Bitmap[] bitmaps2 = {
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans2
.get(0).getWidth(), (int) bitmapBeans2.get(0).getWidth()),
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans2
.get(0).getWidth(), (int) bitmapBeans2.get(0).getWidth())};
Bitmap[] bitmaps3 = {
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans3
.get(0).getWidth(), (int) bitmapBeans3.get(0).getWidth()),
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans3
.get(0).getWidth(), (int) bitmapBeans3.get(0).getWidth()),
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans3
.get(0).getWidth(), (int) bitmapBeans3.get(0).getWidth())};
Bitmap[] bitmaps4 = {
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans4
.get(0).getWidth(), (int) bitmapBeans4.get(0).getWidth()),
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans4
.get(0).getWidth(), (int) bitmapBeans4.get(0).getWidth()),
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans4
.get(0).getWidth(), (int) bitmapBeans4.get(0).getWidth()),
ThumbnailUtils.extractThumbnail(BitmapUtils.getScaleBitmap(
getResources(), R.drawable.j), (int) bitmapBeans4
.get(0).getWidth(), (int) bitmapBeans4.get(0).getWidth())};
}
~~~
4.组合bitmap图片(也就是将我们的图片用Canvas画到一起)
~~~
/**
* 获得合在一起的bitmap
* @param mEntityList
* @param bitmaps
* @return
*/
public static Bitmap getCombineBitmaps(List<BitmapBean> mEntityList,
Bitmap... bitmaps) {
Bitmap newBitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
for (int i = 0; i < mEntityList.size(); i++) {
bitmaps[i] = GetRoundedCornerBitmap(bitmaps[i]);
newBitmap = mixtureBitmap(newBitmap, bitmaps[i], new PointF(
mEntityList.get(i).getX(), mEntityList.get(i).getY()));
}
return newBitmap;
}
~~~
这里我为了好看将图片设置成圆形的了
~~~
/**
* 获取圆形的bitmap
* @param bitmap
* @return
*/
public static Bitmap GetRoundedCornerBitmap(Bitmap bitmap) {
try {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(),
bitmap.getHeight());
final RectF rectF = new RectF(new Rect(0, 0, bitmap.getWidth(),
bitmap.getHeight()));
final float roundPx = 50;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.BLACK);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
final Rect src = new Rect(0, 0, bitmap.getWidth(),
bitmap.getHeight());
canvas.drawBitmap(bitmap, src, rect, paint);
return output;
} catch (Exception e) {
return bitmap;
}
}
~~~
最后开画
~~~
/**
* 画bitmap
* @param first
* @param second
* @param fromPoint
* @return
*/
public static Bitmap mixtureBitmap(Bitmap first, Bitmap second,
PointF fromPoint) {
if (first == null || second == null || fromPoint == null) {
return null;
}
Bitmap newBitmap = Bitmap.createBitmap(first.getWidth(),
first.getHeight(), Bitmap.Config.ARGB_8888);
Canvas cv = new Canvas(newBitmap);
cv.drawBitmap(first, 0, 0, null);
cv.drawBitmap(second, fromPoint.x, fromPoint.y, null);
cv.save(Canvas.ALL_SAVE_FLAG); //保存全部图层
cv.restore();
return newBitmap;
}
~~~
这样就简单的实现了微信群聊头像的效果,当然需要对图片做一些处理,已防止OOM,你也可以将它自定义成一个View组件,小编有时间的话会实现这个的。
最后再给大家看一下小编项目上实现的效果吧,没啥区别,只不多数据源不一样了,是从网络上获取的。
小编已经把代码上传导github上了,求大家star啊
[https://github.com/Hankkin/WeixinGroupIconDemo](https://github.com/Hankkin/WeixinGroupIconDemo)
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed815813.jpg "")
Android 百度地图SDK 实现获取周边位置POI
最后更新于:2022-04-01 15:54:49
### 绪论
好久没写博客了,最近比较忙,闲下来写写博客,把小编最近用过的东西跟大家分享一下,记得上次写过一篇百度地图实现定位功能的博客,今天给大家分享一下定位之后获取周边地理位置的实现。
如果你还不知道怎么定位,看一下这里:
[Android 轻松实现百度地图定位](http://blog.csdn.net/lyhhj/article/details/49129865)
好了先看一下实现效果怎么样吧?
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed779e76.jpg "")
### 实现
### 1.布局文件
~~~
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/height_top_bar"
android:background="@color/common_top_bar_dark"
android:gravity="center_vertical">
<Button
android:id="@+id/btn_location_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:drawableLeft="@drawable/back"
android:text="@string/top_back"
style="@style/btn_title_bar"
android:layout_alignParentLeft="true"
android:onClick="back"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/location_message"
style="@style/txt_titlebar_message"/>
<Button
android:id="@+id/btn_location_ok"
android:layout_width="52dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="@drawable/common_tab_bg"
android:text="@string/txt_queding"
style="@style/btn_title_bar"/>
</RelativeLayout>
<com.baidu.mapapi.map.MapView
android:layout_weight="2"
android:id="@+id/mapview_location"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:clickable="true" />
<ListView
android:layout_weight="3"
android:id="@+id/lv_location_nearby"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
~~~
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed7c8292.jpg "")
布局文件就是上面是一个百度地图的mapview,下面是一个显示周边位置的ListView,很简单。
### 自动定位
我们先看一下根据自己的地理位置实现定位
1.首先初始化要用到的组件
~~~
/**
* 初始化组件
* by黄海杰 at:2015-11-02 17:15:47
*/
private void initView() {
btnLocationBack = (Button) findViewById(R.id.btn_location_back);
btnLocationBack.setOnClickListener(this);
btnLocationOk = (Button) findViewById(R.id.btn_location_ok);
btnLocationOk.setOnClickListener(this);
mapViewLocation = (MapView) findViewById(R.id.mapview_location);
lvLocNear = (ListView) findViewById(R.id.lv_location_nearby);
nearList = new ArrayList<PoiInfo>();
adapter = new LocNearAddressAdapter(context, nearList, isSelected);
lvLocNear.setAdapter(adapter);
}
~~~
2.初始化LocationClient类,该类需要在主线程中声明
~~~
public LocationClient mLocationClient = null;
public BDLocationListener myListener = new MyLocationListener();
public void onCreate() {
mLocationClient = new LocationClient(getApplicationContext()); //声明LocationClient类
mLocationClient.registerLocationListener( myListener ); //注册监听函数
}
~~~
3.配置定位SDK参数
设置定位参数包括:定位模式(高精度定位模式,低功耗定位模式和仅用设备定位模式),返回坐标类型,是否打开GPS,是否返回地址信息、位置语义化信息、POI信息等等。
LocationClientOption类,该类用来设置定位SDK的定位方式
~~~
private void initLocation(){
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationMode.Hight_Accuracy
);//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
option.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系
int span=1000;
option.setScanSpan(span);//可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的
option.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要
option.setOpenGps(true);//可选,默认false,设置是否使用gps
option.setLocationNotify(true);//可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
option.setIsNeedLocationDescribe(true);//可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近”
option.setIsNeedLocationPoiList(true);//可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到
option.setIgnoreKillProcess(false);//可选,默认false,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认杀死
option.SetIgnoreCacheException(false);//可选,默认false,设置是否收集CRASH信息,默认收集
option.setEnableSimulateGps(false);//可选,默认false,设置是否需要过滤gps仿真结果,默认需要
mLocationClient.setLocOption(option);
}
~~~
4.实现BDLocationListener接口
~~~
/**
* 监听函数,有新位置的时候,格式化成字符串,输出到屏幕中
*/
public class MyLocationListenner implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
if (location == null) {
return;
}
Log.d("map", "On location change received:" + location);
Log.d("map", "addr:" + location.getAddrStr());
if (progressDialog != null) {
progressDialog.dismiss();
}
if (lastLocation != null) {
if (lastLocation.getLatitude() == location.getLatitude() && lastLocation.getLongitude() == location.getLongitude()) {
Log.d("map", "same location, skip refresh");
// mMapView.refresh(); //need this refresh?
return;
}
}
lastLocation = location;
mBaiduMap.clear();
mCurrentLantitude = lastLocation.getLatitude();
mCurrentLongitude = lastLocation.getLongitude();
Log.e(">>>>>>>", mCurrentLantitude + "," + mCurrentLongitude);
LatLng llA = new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude());
CoordinateConverter converter = new CoordinateConverter();
converter.coord(llA);
converter.from(CoordinateConverter.CoordType.COMMON);
LatLng convertLatLng = converter.convert();
OverlayOptions ooA = new MarkerOptions().position(convertLatLng).icon(BitmapDescriptorFactory
.fromResource(R.drawable.icon_marka))
.zIndex(4).draggable(true);
mCurrentMarker = (Marker) mBaiduMap.addOverlay(ooA);
MapStatusUpdate u = MapStatusUpdateFactory.newLatLngZoom(convertLatLng, 16.0f);
mBaiduMap.animateMapStatus(u);
new Thread(new Runnable() {
@Override
public void run() {
searchNeayBy();
}
}).start();
}
public void onReceivePoi(BDLocation poiLocation) {
if (poiLocation == null) {
return;
}
}
}
~~~
这里接受到的BDLocation中包含好多参数,相信总有一个对你有用的。
### 根据经纬度定位
这种方法不需要自动定位,就是根据经纬度来显示地图上的位置
~~~
/*
* 显示经纬度的位置
* by:hankkin at:2015-05-04
* */
private void showMap(double latitude, double longtitude, String address) {
// sendButton.setVisibility(View.GONE);
LatLng llA = new LatLng(latitude, longtitude);
CoordinateConverter converter = new CoordinateConverter();
converter.coord(llA);
converter.from(CoordinateConverter.CoordType.COMMON);
LatLng convertLatLng = converter.convert();
OverlayOptions ooA = new MarkerOptions().position(convertLatLng).icon(BitmapDescriptorFactory.fromResource(R.drawable.icon_marka))
.zIndex(4).draggable(true);
markerA = (Marker) (mBaiduMap.addOverlay(ooA));
u = MapStatusUpdateFactory.newLatLngZoom(convertLatLng, 16.0f);
mBaiduMap.animateMapStatus(u);
new Thread(new Runnable() {
@Override
public void run() {
searchNeayBy();
}
}).start();
}
~~~
### 获取周边地理位置
最后看一下怎么获取周边的地理位置,这里需要用到SDK中的一个类PoiNearbySearchOption,我们可以看一下类参考:
> PoiNearbySearchOption keyword(java.lang.String key)
检索关键字
PoiNearbySearchOption location(LatLng location)
检索位置
PoiNearbySearchOption pageCapacity(int pageCapacity)
设置每页容量,默认为每页10条
PoiNearbySearchOption pageNum(int pageNum)
分页编号
PoiNearbySearchOption radius(int radius)
设置检索的半径范围
PoiNearbySearchOption sortType(PoiSortType sortType)
搜索结果排序规则,可选,默认
这里是它的一些方法,我们可以看到我们只需要设置一下关键字、周边位置半径、检索位置、排序规则、分页号、每页数量等。然后我们实现OnGetPoiSearchResultListener这个接口,获取周边地理位置结果。
~~~
/**
* 搜索周边地理位置
* by hankkin at:2015-11-01 22:54:49
*/
private void searchNeayBy() {
PoiNearbySearchOption option = new PoiNearbySearchOption();
option.keyword("写字楼");
option.sortType(PoiSortType.distance_from_near_to_far);
option.location(new LatLng(mCurrentLantitude, mCurrentLongitude));
if (radius != 0) {
option.radius(radius);
} else {
option.radius(1000);
}
option.pageCapacity(20);
mPoiSearch.searchNearby(option);
}
~~~
~~~
/**
* 接受周边地理位置结果
* by hankkin at:2015-11-02 17:14:54
*
* @param poiResult
*/
@Override
public void onGetPoiResult(PoiResult poiResult) {
if (poiResult != null) {
if (poiResult.getAllPoi()!=null&&poiResult.getAllPoi().size()>0){
nearList.addAll(poiResult.getAllPoi());
if (nearList != null && nearList.size() > 0) {
for (int i = 0; i < nearList.size(); i++) {
isSelected.put(i, false);
}
}
Message msg = new Message();
msg.what = 0;
handler.sendMessage(msg);
}
}
}
~~~
获取完数据之后更新适配器显示周边位置就OK了,最后再实现一个小小的功能,就是点击列表中的每个位置,显示位置的小图标根据位置的改变而改变
~~~
/**
* 周边地理位置列表点击事件
* by hankkin at:2015-11-02 17:16:29
*/
lvLocNear.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
adapter.setSelected(i);
adapter.notifyDataSetChanged();
PoiInfo ad = (PoiInfo) adapter.getItem(i);
u = MapStatusUpdateFactory.newLatLng(ad.location);
mBaiduMap.animateMapStatus(u);
if (!isLoc) {
mCurrentMarker.setPosition(ad.location);
} else {
markerA.setPosition(ad.location);
}
}
});
~~~
好了,很简单有用的一个小功能,会给用户带来很好的体验效果。
小编今天就写这么多,以后会更多的给大家分享的。
Android高仿微信照片选择器+预览+显示照片
最后更新于:2022-04-01 15:54:46
转载请说明出处: http://blog.csdn.net/lyhhj/article/details/49046109
前阵子写过一片博客,是关于选择多图上传的一个小demo,那个demo是从网上找的然后自己修改了一下,那个demo是仿照QQ的选择照片的形式,那么现在用的比较多的是仿照微信选择照片的形式,所以自己就鼓捣了一个高仿微信照片选择的demo,这里跟大家分享一下,建议大家再看这篇博客之前先看一下我之间的选择多图的那篇博客,那里面的代码讲的比较详细,这篇是在那篇基础之上的:
[【Android源码解析】选择多张图片上传多图预览](http://blog.csdn.net/lyhhj/article/details/47731439)
说明一下,这篇博客中的相册选择是参照鸿扬大神的demo写的。
好了下面进入正题,我们先看一下实现效果吧:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed636608.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed64cc76.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed66f113.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed6ed4d3.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed739f75.jpg)
下面来介绍一下代码:
之前的代码就不多说了,上面的那篇博客里面有,说一下鸿扬大神的代码吧
基本思路就是:
1.先到手机中扫描jpeg和png的图片
2.获取导图片的路径和图片的父路径名也就是文件夹名
3.将图片路径和文件夹名分别添加导数据源中
4.数据源有了就是显示了,文件夹显示是利用的popwindow,而图片显示则是GridView
看一下具体代码:
首先开启一个线程去扫描图片
~~~
/**
* 利用ContentProvider扫描手机中的图片,此方法在运行在子线程中 完成图片的扫描,最终获得jpg最多的那个文件夹
*/
private void getImages()
{
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED))
{
Toast.makeText(this, "暂无外部存储", Toast.LENGTH_SHORT).show();
return;
}
// 显示进度条
mProgressDialog = ProgressDialog.show(this, null, "正在加载...");
new Thread(new Runnable()
{
@Override
public void run()
{
String firstImage = null;
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = AlbumActivity.this
.getContentResolver();
// 只查询jpeg和png的图片
Cursor mCursor = mContentResolver.query(mImageUri, null,
MediaStore.Images.Media.MIME_TYPE + "=? or "
+ MediaStore.Images.Media.MIME_TYPE + "=?",
new String[] { "image/jpeg", "image/png" },
MediaStore.Images.Media.DATE_MODIFIED);
Log.e("TAG", mCursor.getCount() + "");
while (mCursor.moveToNext())
{
// 获取图片的路径
String path = mCursor.getString(mCursor
.getColumnIndex(MediaStore.Images.Media.DATA));
Log.e("TAG", path);
// 拿到第一张图片的路径
if (firstImage == null)
firstImage = path;
// 获取该图片的父路径名
File parentFile = new File(path).getParentFile();
if (parentFile == null)
continue;
String dirPath = parentFile.getAbsolutePath();
ImageFloder imageFloder = null;
// 利用一个HashSet防止多次扫描同一个文件夹(不加这个判断,图片多起来还是相当恐怖的~~)
if (mDirPaths.contains(dirPath))
{
continue;
} else
{
mDirPaths.add(dirPath);
// 初始化imageFloder
imageFloder = new ImageFloder();
imageFloder.setDir(dirPath);
imageFloder.setFirstImagePath(path);
}
int picSize = parentFile.list(new FilenameFilter()
{
@Override
public boolean accept(File dir, String filename)
{
if (filename.endsWith(".jpg")
|| filename.endsWith(".png")
|| filename.endsWith(".jpeg"))
return true;
return false;
}
}).length;
totalCount += picSize;
imageFloder.setCount(picSize);
mImageFloders.add(imageFloder);
if (picSize > mPicsSize)
{
mPicsSize = picSize;
mImgDir = parentFile;
}
}
mCursor.close();
// 扫描完成,辅助的HashSet也就可以释放内存了
mDirPaths = null;
// 通知Handler扫描图片完成
mHandler.sendEmptyMessage(0x110);
}
}).start();
}
~~~
代码很详细不多说
文件夹popwindow弹出事件
~~~
private void initEvent()
{
/**
* 为底部的布局设置点击事件,弹出popupWindow
*/
mBottomLy.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
mListImageDirPopupWindow
.setAnimationStyle(R.style.anim_popup_dir);
mListImageDirPopupWindow.showAsDropDown(mBottomLy, 0, 0);
// 设置背景颜色变暗
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.alpha = .3f;
getWindow().setAttributes(lp);
}
});
}
~~~
最后是设置图片的点击事件
~~~
//设置ImageView的点击事件
mImageView.setOnClickListener(new OnClickListener()
{
//选择,则将图片变暗,反之则反之
@Override
public void onClick(View v)
{
// 已经选择过该图片
if (mSelectedImage.contains(mDirPath + "/" + item))
{
mSelectedImage.remove(mDirPath + "/" + item);
mSelect.setImageResource(R.drawable.picture_unselected);
mImageView.setColorFilter(null);
List<ImageBean> delete = new ArrayList<ImageBean>();
for (ImageBean im:Bimp.tempSelectBitmap){
if (im.getPath().equals(mDirPath + "/" + item)){
delete.add(im);
}
}
Bimp.tempSelectBitmap.removeAll(delete);
Message msg = new Message();
msg.what=0;
AlbumActivity.handler.sendMessage(msg);
} else
// 未选择该图片
{
if (Bimp.tempSelectBitmap.size()>8){
Toast.makeText(context,"超出可选图片数",Toast.LENGTH_SHORT).show();
return;
}
else {
mSelectedImage.add(mDirPath + "/" + item);
mSelect.setImageResource(R.drawable.pictures_selected);
mImageView.setColorFilter(Color.parseColor("#77000000"));
ImageBean imageBean = new ImageBean();
imageBean.setPath(mDirPath + "/" + item);
try {
imageBean.setBitmap(Bimp.revitionImageSize(mDirPath + "/" + item));
} catch (IOException e) {
e.printStackTrace();
}
Bimp.tempSelectBitmap.add(imageBean);
Message msg = new Message();
msg.what=0;
AlbumActivity.handler.sendMessage(msg);
}
}
}
~~~
这里面为了配合之前的博客,我加入了选中图片和取消选中图片将图片在Bimp.tempSelectBitmap中删除和添加的操作,更新选择图片的数量,也就是下面这两段代码:
~~~
List<ImageBean> delete = new ArrayList<ImageBean>();
for (ImageBean im:Bimp.tempSelectBitmap){
if (im.getPath().equals(mDirPath + "/" + item)){
delete.add(im);
}
}
Bimp.tempSelectBitmap.removeAll(delete);
Message msg = new Message();
msg.what=0;
AlbumActivity.handler.sendMessage(msg);
~~~
~~~
ImageBean imageBean = new ImageBean();
imageBean.setPath(mDirPath + "/" + item);
try {
imageBean.setBitmap(Bimp.revitionImageSize(mDirPath + "/" + item));
} catch (IOException e) {
e.printStackTrace();
}
Bimp.tempSelectBitmap.add(imageBean);
Message msg = new Message();
msg.what=0;
AlbumActivity.handler.sendMessage(msg);
~~~
这里有一点说明,就是我在写移除图片的时候遇到了一个错误,Java ConcurrentModificationException异常,这个错误就是说当我们的Vector,List或者ArrayList中的数据源发生变化的时候,你再去操作这个list就会出现这个异常错误,解决办法是,遍历这个图片数组,比较路径是否相同(最好的办法是比较id是否相同),new 一个数组将相同的图片假如new的数组中,最后用之前的图片数组removeAll来移除,这样就不会报异常错误了,当然我们new的数组肯定比我们之前的数组数据源少或者等同。
好了代码实现就是这些了,至于源码我整理完毕后就会传上来的,想要的小伙伴可以私聊我。
这里还要非常感谢鸿扬大神写了那么多好的博客,让我们学习,这里有不完善的地方还请小伙伴们多多包涵,多多指正。
代码已经同步到我的Github上,小伙伴们关注一下
[https://github.com/Hankkin/WeiXinSelectImgsDemo.git](https://github.com/Hankkin/WeiXinSelectImgsDemo.git)
转载请说明出处:http://blog.csdn.net/lyhhj/article/details/49046109
Android源码解析-仿今日头条PagerSlidingTabStrip滑动页面导航效果
最后更新于:2022-04-01 15:54:44
最近项目中用到了滑动页面,也就是和目前市场上很火的"今日头条"页面滑动类似,在网上找了一下,大部分都是用ViewPager来实现的,刚开始我用的是ViewPager+ViewGroup,上面的标题按钮用的是HorizontalScrollView,写完之后感觉效果比较生硬,果断换掉,发现了一个效果比较好的第三方,也就是今天的主题:PagerSlidingTabStrip.好了,下面来具体介绍一下PagerSlidingTabStrip,进行一下源码解析.
### 一:到GitHub上去下载源码和Demo
GitHub地址:
https://github.com/astuetz/PagerSlidingTabStrip
先看一下demo的样子吧
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed5d0186.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed600b7e.jpg)
### 二:把PagerSlidingTabStrip导入我们的项目中
然后在我们的布局文件中进行声明:
~~~
<span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.hankkin.PagerSlidingTabStrip"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.hankkin.PagerSlidingTabStrip.view.PagerSlidingTabStrip
android:id="@+id/tab"
app:pstsShouldExpand="false"
app:pstsUnderlineHeight="2dp"
app:pstsIndicatorHeight="2dp"
app:pstsIndicatorColor="@android:color/holo_blue_light"
app:selectedTabTextColor="@android:color/holo_blue_light"
app:pstsDividerColor="@android:color/transparent"
app:pstsTabBackground="@drawable/background"
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="55dp"/>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout></span><span style="font-size:18px;">
</span>
~~~
上面的也就是我们的标题滑动按钮,下面的ViewPager用来存放我们的内容
### 三:创建Adapter
~~~
<span style="font-size:14px;">package com.hankkin.PagerSlidingTabStrip.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import com.hankkin.PagerSlidingTabStrip.fragment.AndroidFragment;
import com.hankkin.PagerSlidingTabStrip.fragment.JavaFragment;
import com.hankkin.PagerSlidingTabStrip.fragment.ObjectCFragment;
import java.util.List;
/**
* Created by Hankkin on 15/9/20.
*/
public class MyAdapter extends FragmentPagerAdapter {
private AndroidFragment androidFragment;
private JavaFragment javaFragment;
private ObjectCFragment objectCFragment;
private String[] titles;
public MyAdapter(FragmentManager fm,String[] titles) {
super(fm);
this.titles = titles;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
if (androidFragment == null) {
androidFragment = new AndroidFragment();
}
return androidFragment;
case 1:
if (javaFragment == null) {
javaFragment = new JavaFragment();
}
return javaFragment;
case 2:
if (objectCFragment == null) {
objectCFragment = new ObjectCFragment();
}
return objectCFragment;
default:
return null;
}
}
@Override
public int getCount() {
return titles.length;
}
public String getPageTitle(int i){
return titles[i];
}
}
</span>
~~~
我这里用的是碎片Fragment,最下面的getPageTitle()是我们的PagerSlidingTabStrip中的方法,用来获取标题
接下来我们初始化PagerSlidingTabStrip和ViewPager还有我们的碎片
~~~
<span style="font-size:14px;"> private void initViews(){
fragments = new ArrayList<>();
pagerTab = (PagerSlidingTabStrip) findViewById(R.id.tab);
pager = (ViewPager) findViewById(R.id.pager);
for (int i=0;i<titles.length;i++){
Fragment fragment = new Fragment();
fragments.add(fragment);
}
adapter = new MyAdapter(getSupportFragmentManager(),titles);
pager.setAdapter(adapter);
pagerTab.setViewPager(pager);
}</span>
~~~
在Adapter中利用碎片管理器获取我们的碎片和标题相对应上.这里面需要注意提一下,如果碎片的个数比较少我们可以手动创建碎片,如果像今日头条很多的话也没关系,动态创建Fragment,因为我们可以看到头条的每个碎片中的内容都是类似的,所以说动态创建也未尝不可,只有个别特殊的我们也可以特殊处理.
就这么简单就可以实现类似头条的页面滑动效果,下面我们来看一下PagerSlidingTabStrip的源码吧,看懂了之后也方便我们进行改进.
### 四:PagerSlidingTabStrip源码解析
首先我们看几个比较重要的属性
~~~
private int indicatorColor = 0xFF666666;// 滑动指示器颜色
private int underlineColor = 0x1A000000;//在视图的底部的全宽度的线pstsunderlinecolor颜色
private int dividerColor = 0x1A000000;//选项卡之间的分隔pstsdividercolor颜色
private boolean shouldExpand = false;//pstsshouldexpand如果设置为TRUE,每个标签都给予同样的重量,默认为false
private boolean textAllCaps = true;//pststextallcaps如果为真,所有选项卡标题都是大写,默认为true
private int scrollOffset = 52;//pstsscrolloffset卷轴被选择的标签的偏移
private int indicatorHeight = 8;//滑动指示器pstsindicatorheight
private int underlineHeight = 2;//在视图的底部的全宽度的线pstsunderlineheight高度
private int dividerPadding = 12;//pstsdividerpadding顶部和底部填充的分频器
private int tabPadding = 24;//pststabpaddingleftright左、右填充每个选项卡
private int dividerWidth = 1;//选项卡分割线宽度
private int tabTextSize = 12;//选项卡字体大小
private int tabTextColor = 0xFF666666;//选项卡字体颜色
private int selectedTabTextColor = 0xFF666666;//当前选中字体颜色
private Typeface tabTypeface = null;
private int tabTypefaceStyle = Typeface.NORMAL;
private int lastScrollX = 0;
private int tabBackgroundResId = R.drawable.background;//pststabbackground背景绘制的每个标签,应该是一个statelistdrawable
~~~
我们可以自己定义这些属性的值,因为可能默认的不太好看
1.首先看一下我们的构造会初始化一些什么属性
~~~
<span style="font-size:14px;">public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setFillViewport(true);//默认使子view可以拉伸来填满整个屏幕
setWillNotDraw(false);//默认不执行OnDraw()方法
//初始化盛放按钮标题的线性布局
tabsContainer = new LinearLayout(context);
tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(tabsContainer);
DisplayMetrics dm = getResources().getDisplayMetrics();
//导入相应资源文件
scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm);
indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm);
underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm);
dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm);
tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm);
dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm);
tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm);
// get system attrs (android:textSize and android:textColor)
TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
tabTextSize = a.getDimensionPixelSize(0, tabTextSize);
tabTextColor = a.getColor(1, tabTextColor);
a.recycle();
// get custom attrs
a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip);
indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, indicatorColor);
//tab文字选中时的颜色,默认和滑动指示器的颜色一致
selectedTabTextColor=a.getColor(R.styleable.PagerSlidingTabStrip_selectedTabTextColor, indicatorColor);
//初始化属性样式
underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, underlineColor);
dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, dividerColor);
indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, indicatorHeight);
underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsUnderlineHeight, underlineHeight);
dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerPadding, dividerPadding);
tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight, tabPadding);
tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_pstsTabBackground, tabBackgroundResId);
shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, shouldExpand);
scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsScrollOffset, scrollOffset);
textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps);
a.recycle();
rectPaint = new Paint();
rectPaint.setAntiAlias(true);
rectPaint.setStyle(Style.FILL);
dividerPaint = new Paint();
dividerPaint.setAntiAlias(true);
dividerPaint.setStrokeWidth(dividerWidth);
defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
if (locale == null) {
locale = getResources().getConfiguration().locale;
}
}</span>
~~~
这些基本都能看懂,主要都是初始化一些属性,代码中也有相应的注释,就不一一介绍了
2.定义ViewPager滑动监听器,设置当前pager的位置
~~~
<span style="font-size:14px;">private class PageListener implements OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
currentPosition = position;
currentPositionOffset = positionOffset;
scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));
invalidate();
if (delegatePageListener != null) {
delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
scrollToChild(pager.getCurrentItem(), 0);
}
if (delegatePageListener != null) {
delegatePageListener.onPageScrollStateChanged(state);
}
}
@Override
public void onPageSelected(int position) {
selectedPosition = position;
updateTabStyles();
if (delegatePageListener != null) {
delegatePageListener.onPageSelected(position);
}
}
}</span>
~~~
3.更新标题样式,也就是滑动到当前标题下设置标题状态
~~~
<span style="font-size:14px;">private void updateTabStyles() {
for (int i = 0; i < tabCount; i++) {
View v = tabsContainer.getChildAt(i);
v.setBackgroundResource(tabBackgroundResId);
if (v instanceof TextView) {
TextView tab = (TextView) v;
tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize);
tab.setTypeface(tabTypeface, tabTypefaceStyle);
tab.setTextColor(tabTextColor);
// setAllCaps() is only available from API 14, so the upper case is made manually if we are on a
// pre-ICS-build
if (textAllCaps) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
tab.setAllCaps(true);
} else {
tab.setText(tab.getText().toString().toUpperCase(locale));
}
}
if (i == selectedPosition) {
tab.setTextColor(selectedTabTextColor);
}
}
}
}</span>
~~~
大体就差不多这些了,好了,给大家看一下我自己完善后的样子吧
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-18_57144ed61cd13.jpg)
没有做太多调整,只是稍微的改了一下样式,里面还有一些比较细节的东西没有介绍,比如标题的宽度是平均分配还是分体字体长度进行设置,我们可以修改shouldExpand属性
大家有兴趣的自己仔细研究一下吧.
前言
最后更新于:2022-04-01 15:54:42
> 原文出处:[Android 高仿篇](http://blog.csdn.net/column/details/hankkin-imitation.html)
作者:[lyhhj](http://blog.csdn.net/lyhhj)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# Android 高仿篇
> 高仿热门App中的各种功能模块