編輯:關於Android編程
大家第一眼肯定想到了Gallery,這是最早android圖庫自帶的效果,現在基本不用,那有沒有其他好的辦法呢?我們首先考慮的還是ViewPager+PagerAdapter的實現策略。
後面在網上了搜了一下, 發現要實現上面的效果,我們需要注意兩個方面,首先是怎麼在兩邊顯示兩個小圖,第二,怎麼實現無限滑動。
1,首先就是用到了View的android:clipChildren屬性,.簡單來說父View是默認是束縛子View 的顯示范圍的,所以當我們在父View有 padding , 那麼 子View 則在 padding區域是不能顯示內容的。當設置android:clipChildren="false"的時候,子View 就可以在父View 的padding內容區域顯示內容了。
2,實現無限循環很簡單,網上也有很多的解決方案,我這裡不考慮性能上的東西,且看下面簡單的代碼:
private class ImageAdapter extends PagerAdapter{
private ArrayList viewlist;
public ImageAdapter(ArrayList viewlist) {
this.viewlist = viewlist;
}
@Override
public int getCount() {
//設置成最大,使用戶看不到邊界,大家可以去查詢下這個大小
return Integer.MAX_VALUE;
}
@Override
public void destroyItem(ViewGroup container, int position,
Object object) {
//注:不要在這裡調用removeView
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
//對ViewPager頁號求模取出View列表中要顯示的項
position %= viewlist.size();
if (position<0){
position = viewlist.size()+position;
}
//這裡是view
ViewHolder viewHolder = null;
View view = LayoutInflater.from(mContext).inflate(
R.layout.item_finefare_layout, null);
if (viewHolder == null) {
viewHolder = new ViewHolder(view);
}
bindView(viewHolder, data);
container.addView(view, LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
return view;
}
}
上面代碼應該注意的幾點:
getCount()方法的返回值:這個值直接關系到ViewPager的“邊界”,因此當我們把它設置為Integer.MAX_VALUE之後,用戶基本就看不到這個邊界了(估計滑到這裡的時候電池已經掛了吧o_O)。當然,通常情況下設置為100倍實際內容個數也是可以的,之前看的某個實現就是這麼干的。
instantiateItem()方法position的處理:由於我們設置了count為Integer.MAX_VALUE,因此這個position的取值范圍很大很大,但我們實際要顯示的內容肯定沒這麼多(往往只有幾項),所以這裡肯定會有求模操作。但是,簡單的求模會出現問題:考慮用戶向左滑的情形,則position可能會出現負值。所以我們需要對負值再處理一次,使其落在正確的區間內。
instantiateItem()方法父組件的處理:通常我們會直接addView,但這裡如果直接這樣寫,則會拋出IllegalStateException。假設一共有三個view,則當用戶滑到第四個的時候就會觸發這個異常,原因是我們試圖把一個有父組件的View添加到另一個組件。
經過上面的解釋,我們已經很清楚了,以下是代碼的詳細實現,數據來源於網上,大家可以自行模擬:
public class WelfareAdapter extends PagerAdapter {
private Context mContext;
private List dataList = new ArrayList<>();
public WelfareAdapter(Context mContext) {
this.mContext = mContext;
}
public void setDatas(List list) {
if (list.size() <= 0) {
dataList.clear();
notifyDataSetChanged();
return;
}
dataList.clear();
dataList.addAll(list);
notifyDataSetChanged();
}
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
position %= dataList.size();
if (position<0){
position = dataList.size()+position;
}
PanicBean data = dataList.get(position);
ViewHolder viewHolder = null;
View view = LayoutInflater.from(mContext).inflate(
R.layout.item_finefare_layout, null);
if (viewHolder == null) {
viewHolder = new ViewHolder(view);
}
bindView(viewHolder, data);
container.addView(view, LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
return view;
}
private void bindView(ViewHolder viewholder, final PanicBean data) {
Glide.with(mContext).load(data.pic).into(viewholder.welfareImage);
viewholder.welfareImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ToastUtils.showToast("你點擊了"+data.href);
}
});
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// container.removeView((View) object);
}
class ViewHolder {
@BindView(R.id.welfare_image)
RoundedImageView welfareImage;
ViewHolder(View view) {
ButterKnife.bind(this, view);
view.setTag(this);
}
public void reset() {
welfareImage.setBackground(mContext.getResources().getDrawable(R.drawable.welfare_default_icon));
}
}
}
用到的布局:
public class FineFareEntity {
public List panic;
public static class PanicBean {
public String id;
public long endtime;
public String pic;
public int type;
public String href;
public String title;
public String share;
}
}
為了方便使用我們都自定義成View,方便以後代碼維護:
public class WelfareView extends SimpleLinearLayout {
@BindView(R.id.finefare_count)
TextView finefareCount;
@BindView(R.id.viewPager)
ViewPager viewPager;
@BindView(R.id.finefare_name)
TextView finefareName;
@BindView(R.id.welfare_view)
LinearLayout welfareView;
private WelfareAdapter adapter = null;
private List welfareList = null;
public WelfareView(Context context) {
super(context);
}
public WelfareView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void initViews() {
contentView = inflate(mContext, R.layout.layout_welfare, this);
ButterKnife.bind(this);
initViewPager();
initTouch();
}
private void initTouch() {
//這裡要把父類的touch事件傳給子類,不然邊上的會滑不動
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return viewPager.dispatchTouchEvent(event);
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
position %= welfareList.size();
if (position<0){
position = welfareList.size()+position;
}
finefareName.setText(welfareList.get(position).id);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void initViewPager() {
viewPager.setOffscreenPageLimit(3);
viewPager.setPageTransformer(true, new ScalePagerTransformer());
//設置Pager之間的間距
viewPager.setPageMargin(UIUtils.dp2px(mContext, 15));
adapter = new WelfareAdapter(mContext);
viewPager.setAdapter(adapter);
}
public void setWelfareData(List datas) {
this.welfareList = datas;
welfareView.setVisibility(datas.size()>0?VISIBLE:GONE);
finefareCount.setText("共有" + datas.size() + "個福利");
finefareName.setText(welfareList.get(getCurrentDisplayItem()).title);
adapter = new WelfareAdapter(mContext);
adapter.setDatas(datas);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(adapter.getCount() > 0 ? 1 : 0, true);
}
public int getCurrentDisplayItem() {
if (viewPager != null) {
return viewPager.getCurrentItem();
}
return 0;
}
}
這裡有一個滑動縮放的類:
public class ScalePagerTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
@Override
public void transformPage(View view, float position) {
if (position >= -1 || position <= 1) {
final float height = view.getHeight();
final float width = view.getWidth();
final float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
final float vertMargin = height * (1 - scaleFactor) / 2;
final float horzMargin = width * (1 - scaleFactor) / 2;
view.setPivotY(0.5f * height);
view.setPivotX(0.5f * width);
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
}
}
}
SimpleLinearLayout 類
public class SimpleLinearLayout extends LinearLayout {
protected Context mContext;
protected View contentView;
protected AtomicBoolean isPreparingData;
public SimpleLinearLayout(Context context) {
super(context);
this.mContext = context;
isPreparingData = new AtomicBoolean(false);
initViews();
}
public SimpleLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
isPreparingData = new AtomicBoolean(false);
initViews();
}
protected void initViews() {
}
}
android:clipChildren="false" android:layout_marginTop="10dp" android:background="@color/c12" android:orientation="vertical">
private void initWelfare() {
String welfare = FileUtils.readAssert(getActivity(), "welfare.txt");
FineFareEntity entity=JsonUtils.parseJson(welfare,FineFareEntity.class);
if (entity!=null){
welfareView.setWelfareData(entity.panic);
}
}
涉及到的布局:
public static String readAssert(Context context, String fileName){
String resultString="";
try {
InputStream inputStream=context.getResources().getAssets().open(fileName);
byte[] buffer=new byte[inputStream.available()];
inputStream.read(buffer);
resultString=new String(buffer,"utf-8");
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
}
最後為了方便大家的模擬,我把數據給大家,有點多,大家自己放到assert目錄下,
{
panic: [
{
id: "2412",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M04/E3/67/CgvUBVe9vyOAV47CAACXZZs5GrU558_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n770",
title: "今日限時搶",
share: ""
},
{
id: "2417",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M09/E4/37/CgvUA1e91VmAMYrwAAC5qcblOUg650_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n781",
title: "今日限時搶",
share: ""
},
{
id: "2413",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M05/E3/D4/CgvUA1e9v2SAVf3qAAB9GcBIWYA268_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n771",
title: "今日限時搶",
share: ""
},
{
id: "2414",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M05/E3/69/CgvUBVe9v4aAMaFRAABWy73vn2g252_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n772",
title: "今日限時搶",
share: ""
},
{
id: "2415",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M06/E3/02/CgvUBFe9v6WAP85NAAC6EK5e5Vg469_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n773",
title: "今日限時搶",
share: ""
},
{
id: "2416",
endtime: 1472097600000,
pic: "http://pc1.img.ymatou.com/G02/M06/E3/02/CgvUBFe9v8CAHyXVAACELcKFT_M328_o.jpg",
type: 1,
href: "http://evt.ymatou.com/n775",
title: "今日限時搶",
share: ""
}
]
}
Android 6.0+ 運行時權限探索
引言:去年Android 6.0發布後,其新引入的(Requesting Permissions at Run Time)運行時權限就備受開發者關注,隨著今年國內手機廠商
Android應用之——最新版本SDK V2.4實現QQ第三方登錄
為什麼要寫這篇博客呢?因為,我在做這個第三方登錄的時候,找了很多資料,發現要麼就是過時了,要麼就是說的很不清楚,很羅嗦,而且很多都是一些小demo,不是什麼實例,甚至連騰
Android UI開發神兵利器之Android Action Bar Style Generator
ActionBar是3.0後的UI設計規范,同時也是Google極力推薦使用的設計風格,如何快速設計一個入眼的ActionBar呢,更進一步,給我們搭好一個入眼的Acti
Android仿微信列表滑動刪除之可滑動控件(一)
這次是列表滑動刪除的第三波,仿微信的列表滑動刪除。先上個效果圖: 前面的文章裡面說過開源框架SwipeListView的實現原理是每個列表item中