Android开发笔记(一百)折叠式列表

更多动态视图MoreNewsView

经常看朋友圈的动态,有的动态内容较多就只展示前面一段,如果用户想看完整的再点击展开,这样整个页面的动态列表比较均衡,不会出现个别动态占用大片屏幕的情况。同样,查看博客的文章列表也类似,只展示文章开头几行内容,有需要再点击加载全篇文章。

动态列表直接使用ListView,动态内容就得自己写个控件了,自定义控件的难点在于如何把握动态下拉和收起的动画。这里我们要先预习TextView的相关函数,下面是本文用到的方法说明:

getHeight : 获取TextView的显示高度。

setHeight : 设置TextView的显示高度。

getLineHeight : 获取每行文本的高度。

getLineCount : 获取所有文本的行数。

如果一开始每条动态默认显示四行,那么默认显示高度是getLineHeight*4,使用setHeight方法即可设置动态的初始显示高度。点击展开动态全文时,就得显示所有行的文本,整个文本的高度是getLineHeight*getLineCount。现在有了每条动态的初始高度,以及动态全文的完整高度,再加个拉伸动画就差不多了。拉伸动画的主要工作是随着时间的推移,给TextView设置渐增或渐减的高度,这要重写Animation的applyTransformation方法。

下面是点击监听器的显示动画代码示例:

	private OnClickListener mOnClickListener = new View.OnClickListener() {
		boolean isExpand;
		@Override
		public void onClick(View v) {
			tv_expand.setText(isExpand?"查看全文":"收起关注");
			isExpand = !isExpand;
			tv_content.clearAnimation();
			final int deltaValue;
			final int startValue = tv_content.getHeight();
			int durationMillis = 300;
			if (isExpand) {
				deltaValue = tv_content.getLineHeight() * tv_content.getLineCount() - startValue;
			} else {
				deltaValue = tv_content.getLineHeight() * maxLine - startValue;
			}
			Animation animation = new Animation() {
				protected void applyTransformation(float interpolatedTime, Transformation t) {
					tv_content.setHeight((int) (startValue + deltaValue * interpolatedTime));
				}
			};
			animation.setDuration(durationMillis);
			tv_content.startAnimation(animation);
		}
	};

下面是展开/收起朋友圈动态详情的效果截图

可折叠列表ExpandableListView

嵌套列表ExpandableListView是又一种常见的控件,常见的业务场景包括:好友分组与好友列表、订单列表与订单内的商品列表、邮件夹分组与邮件列表等等。

ExpandableListView常用方法

Android自带的ExpandableListView可以直接用于嵌套列表,点击一个组,展开该组下的子列表;再点击这个组,收起该组下的子列表。

下面是ExpandableListView的常用方法说明:

setAdapter : 设置适配器。适配器类型为ExpandableListAdapter

expandGroup : 展开指定分组。

collapseGroup : 收起指定分组。

isGroupExpanded : 判断指定分组是否展开。

setSelectedGroup : 设置选中的分组。

setSelectedChild : 设置选中的子项。

setGroupIndicator : 设置指定分组的指示图像。

setChildIndicator : 设置指定子项的指示图像。

ExpandableListView监听器

除了OnItemClickListener,ExpandableListView新加了下面几个监听器:

1、分组展开事件,相关类名与方法说明如下:

监听器类名 : OnGroupExpandListener

设置监听器的方法 : setOnGroupExpandListener

监听器需要重写的点击方法 : onGroupExpand

2、分组收起事件,相关类名与方法说明如下:

监听器类名 : OnGroupCollapseListener

设置监听器的方法 : setOnGroupCollapseListener

监听器需要重写的点击方法 : onGroupCollapse

3、分组点击事件,相关类名与方法说明如下:

监听器类名 : OnGroupClickListener

设置监听器的方法 : setOnGroupClickListener

监听器需要重写的点击方法 : onGroupClick

4、子项点击事件,相关类名与方法说明如下:

监听器类名 : OnChildClickListener

设置监听器的方法 : setOnChildClickListener

监听器需要重写的点击方法 : onChildClick

ExpandableListView适配器

ExpandableListAdapter是ExpandableListView的专用适配器,它并不继承自其他适配器。

下面是ExpandableListAdapter经常要重写的几个方法:

getGroupCount : 获取分组的个数。

getChildrenCount : 获取子项的个数。

getGroupView : 获取指定分组的视图。

getChildView : 获取指定子项的视图。

isChildSelectable : 判断子项是否允许选择。

ExpandableListView常见问题

ExpandableListView有时会发现子项不会响应点击事件,这可能是某个环节没有正确设置。要让子项目响应点击事件,需满足下面三个条件:

1、ExpandableListAdapter适配器的isChildSelectable方法要返回true;

2、ExpandableListView对象要注册监听器setOnChildClickListener,并重写onChildClick方法;

3、子项目中若有Button、EditText等默认占用焦点的控件,要去除焦点占用,即setFocusable和setFocusableInTouchMode设置为false;

下面是ExpandableListView的一个应用例子效果截图(电子邮箱):

下面是运用ExpandableListView的代码示例:

适配器代码

import java.util.ArrayList;

import com.example.exmfoldlist.R;
import com.example.exmfoldlist.bean.MailBox;
import com.example.exmfoldlist.bean.MailItem;

import android.content.Context;
import android.database.DataSetObserver;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class CustomExpandAdapter implements ExpandableListAdapter,OnGroupClickListener,OnChildClickListener {

	private final static String TAG = "CustomExpandAdapter";
	private LayoutInflater mInflater;
	private Context mContext;
	private ArrayList<MailBox> mBoxList;

	public CustomExpandAdapter(Context context, ArrayList<MailBox> box_list) {
		mInflater = LayoutInflater.from(context);
		mContext = context;
		mBoxList = box_list;
	}

	@Override
	public void registerDataSetObserver(DataSetObserver observer) {
	}

	@Override
	public void unregisterDataSetObserver(DataSetObserver observer) {
	}

	@Override
	public int getGroupCount() {
		return mBoxList.size();
	}

	@Override
	public int getChildrenCount(int groupPosition) {
		return mBoxList.get(groupPosition).mail_list.size();
	}

	@Override
	public Object getGroup(int groupPosition) {
		return mBoxList.get(groupPosition);
	}

	@Override
	public Object getChild(int groupPosition, int childPosition) {
		return mBoxList.get(groupPosition).mail_list.get(childPosition);
	}

	@Override
	public long getGroupId(int groupPosition) {
		return groupPosition;
	}

	@Override
	public long getChildId(int groupPosition, int childPosition) {
		return childPosition;
	}

	@Override
	public boolean hasStableIds() {
		return false;
	}

	@Override
	public View getGroupView(int groupPosition, boolean isExpanded,
			View convertView, ViewGroup parent) {
		ViewHolderBox holder = null;
		if (convertView == null) {
			holder = new ViewHolderBox();
			convertView = mInflater.inflate(R.layout.list_box, null);
			holder.iv_box = (ImageView) convertView.findViewById(R.id.iv_box);
			holder.tv_box = (TextView) convertView.findViewById(R.id.tv_box);
			holder.tv_count = (TextView) convertView.findViewById(R.id.tv_count);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolderBox) convertView.getTag();
		}
		MailBox box = mBoxList.get(groupPosition);
		holder.iv_box.setImageResource(box.box_icon);
		holder.tv_box.setText(box.box_title);
		holder.tv_count.setText(box.mail_list.size()+"封邮件");
		return convertView;
	}

	@Override
	public View getChildView(final int groupPosition, final int childPosition,
			boolean isLastChild, View convertView, ViewGroup parent) {
		ViewHolderMail holder = null;
		if (convertView == null) {
			holder = new ViewHolderMail();
			convertView = mInflater.inflate(R.layout.list_mail, null);
			holder.ck_mail = (CheckBox) convertView.findViewById(R.id.ck_mail);
			holder.tv_date = (TextView) convertView.findViewById(R.id.tv_date);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolderMail) convertView.getTag();
		}
		MailItem item = mBoxList.get(groupPosition).mail_list.get(childPosition);
		holder.ck_mail.setFocusable(false);
		holder.ck_mail.setFocusableInTouchMode(false);
		holder.ck_mail.setText(item.mail_title);
		holder.ck_mail.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				MailBox box = mBoxList.get(groupPosition);
				MailItem item = box.mail_list.get(childPosition);
				String desc = String.format("您点击了%s的邮件,标题是%s", box.box_title, item.mail_title);
				Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show();
			}
		});
		holder.tv_date.setText(item.mail_date);
		return convertView;
	}

	//如果子条目需要响应点击事件,这里要返回true
	@Override
	public boolean isChildSelectable(int groupPosition, int childPosition) {
		return true;
	}

	@Override
	public boolean areAllItemsEnabled() {
		return true;
	}

	@Override
	public boolean isEmpty() {
		return false;
	}

	@Override
	public void onGroupExpanded(int groupPosition) {
	}

	@Override
	public void onGroupCollapsed(int groupPosition) {
	}

	@Override
	public long getCombinedChildId(long groupId, long childId) {
		return 0;
	}

	@Override
	public long getCombinedGroupId(long groupId) {
		return 0;
	}

	public final class ViewHolderBox {
		public ImageView iv_box;
		public TextView tv_box;
		public TextView tv_count;
	}

	public final class ViewHolderMail {
		public CheckBox ck_mail;
		public TextView tv_date;
	}

	@Override
	public boolean onChildClick(ExpandableListView parent, View v,
			int groupPosition, int childPosition, long id) {
		ViewHolderMail holder = (ViewHolderMail) v.getTag();
		holder.ck_mail.setChecked(!(holder.ck_mail.isChecked()));
		return true;
	}

	//如果返回true,就不会展示子列表
	@Override
	public boolean onGroupClick(ExpandableListView parent, View v,
			int groupPosition, long id) {
		String desc = String.format("您点击了%s", mBoxList.get(groupPosition).box_title);
		Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show();
		return false;
	}

}

调用的代码

import java.util.ArrayList;

import com.example.exmfoldlist.adapter.CustomExpandAdapter;
import com.example.exmfoldlist.bean.MailBox;
import com.example.exmfoldlist.bean.MailItem;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ExpandableListView;

public class ExpandActivity extends Activity {
	private final static String TAG = "ExpandActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_expand);

		ExpandableListView elv_list = (ExpandableListView) findViewById(R.id.elv_list);
		final ArrayList<MailBox> box_list = new ArrayList<MailBox>();
		box_list.add(new MailBox(R.drawable.mail_folder_inbox, "收件箱", getRecvMail()));
		box_list.add(new MailBox(R.drawable.mail_folder_outbox, "发件箱", getSentMail()));
		box_list.add(new MailBox(R.drawable.mail_folder_draft, "草稿箱", getDraftMail()));
		box_list.add(new MailBox(R.drawable.mail_folder_recycle, "废件箱", getRecycleMail()));
		CustomExpandAdapter adapter = new CustomExpandAdapter(this, box_list);
		elv_list.setAdapter(adapter);
		elv_list.setOnChildClickListener(adapter);
		elv_list.setOnGroupClickListener(adapter);
		elv_list.expandGroup(0);  //默认展开第一个邮件夹
	}

	private ArrayList<MailItem> getRecvMail() {
		ArrayList<MailItem> mail_list = new ArrayList<MailItem>();
		mail_list.add(new MailItem("这里是收件箱呀1", "2016年3月25日"));
		mail_list.add(new MailItem("这里是收件箱呀2", "2016年3月20日"));
		mail_list.add(new MailItem("这里是收件箱呀3", "2016年3月24日"));
		mail_list.add(new MailItem("这里是收件箱呀4", "2016年3月21日"));
		mail_list.add(new MailItem("这里是收件箱呀5", "2016年3月23日"));
		return mail_list;
	}

	private ArrayList<MailItem> getSentMail() {
		ArrayList<MailItem> mail_list = new ArrayList<MailItem>();
		mail_list.add(new MailItem("邮件发出去了吗1", "2016年3月25日"));
		mail_list.add(new MailItem("邮件发出去了吗2", "2016年3月24日"));
		mail_list.add(new MailItem("邮件发出去了吗3", "2016年3月21日"));
		mail_list.add(new MailItem("邮件发出去了吗4", "2016年3月23日"));
		mail_list.add(new MailItem("邮件发出去了吗5", "2016年3月20日"));
		return mail_list;
	}

	private ArrayList<MailItem> getDraftMail() {
		ArrayList<MailItem> mail_list = new ArrayList<MailItem>();
		mail_list.add(new MailItem("暂时放在草稿箱吧1", "2016年3月24日"));
		mail_list.add(new MailItem("暂时放在草稿箱吧2", "2016年3月21日"));
		mail_list.add(new MailItem("暂时放在草稿箱吧3", "2016年3月25日"));
		mail_list.add(new MailItem("暂时放在草稿箱吧4", "2016年3月20日"));
		mail_list.add(new MailItem("暂时放在草稿箱吧5", "2016年3月23日"));
		return mail_list;
	}

	private ArrayList<MailItem> getRecycleMail() {
		ArrayList<MailItem> mail_list = new ArrayList<MailItem>();
		mail_list.add(new MailItem("啊啊啊,怎么被删除了1", "2016年3月21日"));
		mail_list.add(new MailItem("啊啊啊,怎么被删除了2", "2016年3月23日"));
		mail_list.add(new MailItem("啊啊啊,怎么被删除了3", "2016年3月25日"));
		mail_list.add(new MailItem("啊啊啊,怎么被删除了4", "2016年3月20日"));
		mail_list.add(new MailItem("啊啊啊,怎么被删除了5", "2016年3月24日"));
		return mail_list;
	}

}

折叠式布局FoldingLayout

ExpandableListView对于一般场景的折叠式列表已经够用了,可是它的UI风格略显呆板,如果我们想来个显示特效,比如加上折叠展开的动画,那最好还是自己写个折叠式列表控件。

FoldingLayout便是这样一个开源的折叠式布局控件,它实现了像折纸那样折叠展开和折叠收起的动画。下面是FoldingLayout的常用方法说明:

setFoldFactor : 设置折叠的因子。0表示收起,1表示展开。

setOrientation : 设置折叠的方向。VERTICAL表示垂直方向,HORIZONTAL表示水平方向。

setNumberOfFolds : 设置折叠的页数。

FoldingLayout也提供了折叠事件的监听,相关类名与方法说明如下:

监听器类名 : OnFoldListener

设置监听器的方法 : setFoldListener

监听器需要重写的点击方法 :

onStartFold : 开始折叠时触发。

onFoldingState : 折叠状态变化时触发。

onEndFold : 结束折叠时触发。

下面是运用FoldingLayout的代码示例:

import com.example.exmfoldlist.util.MetricsUtil;
import com.example.exmfoldlist.view.FoldingLayout;
import com.example.exmfoldlist.view.OnFoldListener;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

public class FoldingActivity extends Activity {
    private String TAG_ARROW = "service_arrow";
    private String TAG_ITEM = "service_item";

    private View mBottomView;
    private LinearLayout mTrafficLayout, mLifeLayout, mMedicalLayout, mLiveLayout, mPublicLayout;
    private RelativeLayout mTrafficBarLayout, mLifeBarLayout, mMedicalBarLayout, mLiveBarLayout, mPublicBarLayout;
    private FoldingLayout mTrafficFoldingLayout, mLifeFoldingLayout, mMedicalFoldingLayout, mLiveFoldingLayout, mPublicFoldingLayout;

    private final int FOLD_ANIMATION_DURATION = 1000;
    private int mNumberOfFolds = 3;
    private Handler mHandler = new Handler();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_folding);

        mTrafficLayout = (LinearLayout) findViewById(R.id.traffic_layout);
        mLifeLayout = (LinearLayout) findViewById(R.id.life_layout);
        mMedicalLayout = (LinearLayout) findViewById(R.id.medical_layout);
        mLiveLayout = (LinearLayout) findViewById(R.id.live_layout);
        mPublicLayout = (LinearLayout) findViewById(R.id.public_layout);

        mTrafficBarLayout = (RelativeLayout) findViewById(R.id.traffic_bar_layout);
        mLifeBarLayout = (RelativeLayout) findViewById(R.id.life_bar_layout);
        mMedicalBarLayout = (RelativeLayout) findViewById(R.id.medical_bar_layout);
        mLiveBarLayout = (RelativeLayout) findViewById(R.id.live_bar_layout);
        mPublicBarLayout = (RelativeLayout) findViewById(R.id.public_bar_layout);

        mTrafficFoldingLayout = ((FoldingLayout) mTrafficLayout.findViewWithTag(TAG_ITEM));
        mLifeFoldingLayout = ((FoldingLayout) mLifeLayout.findViewWithTag(TAG_ITEM));
        mMedicalFoldingLayout = ((FoldingLayout) mMedicalLayout.findViewWithTag(TAG_ITEM));
        mLiveFoldingLayout = ((FoldingLayout) mLiveLayout.findViewWithTag(TAG_ITEM));
        mPublicFoldingLayout = ((FoldingLayout) mPublicLayout.findViewWithTag(TAG_ITEM));

        mBottomView = findViewById(R.id.bottom_view);
        initFoldingLayout(mTrafficFoldingLayout, mTrafficBarLayout, mTrafficLayout, mLifeLayout);
        initFoldingLayout(mLifeFoldingLayout, mLifeBarLayout, mLifeLayout, mMedicalLayout);
        initFoldingLayout(mMedicalFoldingLayout, mMedicalBarLayout, mMedicalLayout, mLiveLayout);
        initFoldingLayout(mLiveFoldingLayout, mLiveBarLayout, mLiveLayout, mPublicLayout);
        initFoldingLayout(mPublicFoldingLayout, mPublicBarLayout, mPublicLayout, mBottomView);
        setBarEnabled(false);
        mHandler.postDelayed(mDefaultFold, 150);
    }

    private void initFoldingLayout(final FoldingLayout foldingLayout, View bar, final View thisView, final View nextView) {
    	foldingLayout.setVisibility(View.INVISIBLE);
    	bar.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                handleAnimation(v, foldingLayout, thisView, nextView);
            }
        });
    	foldingLayout.setNumberOfFolds(mNumberOfFolds);
    	animateFold(foldingLayout, 100);
    	setMarginToTop(1, nextView);
    }

    private void setBarEnabled(boolean enabled) {
        mTrafficBarLayout.setEnabled(enabled);
        mLifeBarLayout.setEnabled(enabled);
        mMedicalBarLayout.setEnabled(enabled);
        mLiveBarLayout.setEnabled(enabled);
        mPublicBarLayout.setEnabled(enabled);
        mTrafficFoldingLayout.setFoldFactor(!enabled?0.0f:1.0f);
        mLifeFoldingLayout.setFoldFactor(!enabled?0.0f:1.0f);
        mMedicalFoldingLayout.setFoldFactor(!enabled?0.0f:1.0f);
        mLiveFoldingLayout.setFoldFactor(!enabled?0.0f:1.0f);
        mPublicFoldingLayout.setFoldFactor(!enabled?0.0f:1.0f);
    }

    private Runnable mDefaultFold = new Runnable() {
		@Override
		public void run() {
	        setBarEnabled(true);
	        handleAnimation(mTrafficBarLayout, mTrafficFoldingLayout, mTrafficLayout, mLifeLayout);
		}
    };

    private void handleAnimation(final View bar, final FoldingLayout foldingLayout, View parent, final View nextParent) {
    	foldingLayout.setVisibility(View.VISIBLE);
        final ImageView arrow = (ImageView) parent.findViewWithTag(TAG_ARROW);

        foldingLayout.setFoldListener(new OnFoldListener() {
            @Override
            public void onStartFold(float foldFactor) {
                bar.setClickable(true);
                arrow.setBackgroundResource(R.drawable.service_arrow_up);
                resetMarginToTop(foldingLayout, foldFactor, nextParent);
            }

            @Override
            public void onFoldingState(float foldFactor, float foldDrawHeight) {
                bar.setClickable(false);
                resetMarginToTop(foldingLayout, foldFactor, nextParent);
            }

            @Override
            public void onEndFold(float foldFactor) {
                bar.setClickable(true);
                arrow.setBackgroundResource(R.drawable.service_arrow_down);
                resetMarginToTop(foldingLayout, foldFactor, nextParent);
            }
        });
        animateFold(foldingLayout, FOLD_ANIMATION_DURATION);
    }

    private void resetMarginToTop(View view, float foldFactor, View nextParent) {
        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) nextParent.getLayoutParams();
        lp.topMargin =(int)( - view.getMeasuredHeight() * foldFactor) + MetricsUtil.dip2px(FoldingActivity.this, 10);
        nextParent.setLayoutParams(lp);
    }

    private void setMarginToTop(float foldFactor, View nextParent) {
        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) nextParent.getLayoutParams();
        lp.topMargin =(int)( - MetricsUtil.dip2px(FoldingActivity.this, 135) * foldFactor) + MetricsUtil.dip2px(FoldingActivity.this, 10);
        nextParent.setLayoutParams(lp);
    }

    public void animateFold(FoldingLayout foldLayout, int duration) {
        float foldFactor = foldLayout.getFoldFactor();
        ObjectAnimator animator = ObjectAnimator.ofFloat(foldLayout,
                "foldFactor", foldFactor, foldFactor > 0 ? 0 : 1);
        animator.setRepeatMode(ValueAnimator.REVERSE);
        animator.setRepeatCount(0);
        animator.setDuration(duration);
        animator.setInterpolator(new AccelerateInterpolator());
        animator.start();
    }

}

点此查看Android开发笔记的完整目录

时间: 2024-10-11 07:44:13

Android开发笔记(一百)折叠式列表的相关文章

Android开发笔记(一百零三)地图与定位SDK

集成地图SDK 国内常用的地图SDK就是百度和高德了,二者的用法大同小异,可按照官网上的开发指南一步步来.下面是我在集成地图SDK时遇到的问题说明: 1.点击基本地图功能选项,不能打开地图,弹出"key验证出错!请在AndroidManifest.xml文件中检查key设置的"的红色字提示.查看日志提示"galaxy lib host missing meta-data,make sure you know the right way to integrate galaxy&

Android开发笔记(一百三十四)协调布局CoordinatorLayout

协调布局CoordinatorLayout Android自5.0之后对UI做了较大的提升,一个重大的改进是推出了MaterialDesign库,而该库的基础即为协调布局CoordinatorLayout,几乎所有的design控件都依赖于该布局.协调布局的含义,指的是内部控件互相之前的动作关联,比如在A视图的位置发生变化之时,B视图的位置也按照某种规则来变化,仿佛弹钢琴有了协奏曲一般. 使用CoordinatorLayout时,要注意以下几点:1.导入design库:2.根布局采用androi

Android开发笔记(一百零九)利用网盘实现云存储

网盘存储 个人开发者往往没有自己的后台服务器,但同时又想获取app的运行信息,这就要借助于第三方的网络存储(也叫网盘.云盘.微盘等等).通过让app自动在网盘上存取文件,可以间接实现后台服务器的存储功能,同时开发者也能及时找到app的用户信息. 曾几何时,各大公司纷纷推出免费的个人网盘服务,还开放了文件管理api给开发者调用,一时间涌现了网盘提供商的八大金刚:百度网盘.阿里云.华为网盘.腾讯微云.新浪微盘.360云盘.金山快盘.115网盘.可是好景不长,出于盈利.监管等等因素,各大网盘开放平台要

Android开发笔记(一百二十)两种侧滑布局

SlidingPaneLayout SlidingPaneLayout是Android在android-support-v4.jar中推出的一个可滑动面板的布局,在前面<Android开发笔记(一百零一)滑出式菜单>中,我们提到水平布局时的LinearLayout无法自动左右拉伸,必须借助于手势事件才能拉出左侧隐藏的布局,现在SlidingPaneLayout便是为了解决LinearLayout无法自动拉伸的缺陷.只要我们在布局文件的SlidingPaneLayout节点下定义两个子布局,那么

Android开发笔记(一百零六)支付缴费SDK

第三方支付 第三方支付指的是第三方平台与各银行签约,在买方与卖方之间实现中介担保,从而增强了支付交易的安全性.国内常用的支付平台主要是支付宝和微信支付,其中支付宝的市场份额为71.5%,微信支付的市场份额为15.99%,也就是说这两家垄断了八分之七的支付市场(2015年数据).除此之外,还有几个app开发会用到的支付平台,包括:银联支付,主要用于公共事业缴费,如水电煤.有线电视.移动电信等等的充值:易宝支付,主要用于各种报名考试的缴费,特别是公务员与事业单位招考:快钱,被万达收购,主要用于航空旅

Android开发笔记(一百零一)滑出式菜单

可移动页面MoveActivity 滑出式菜单从界面上看,像极了一个水平滚动视图HorizontalScrollView,当然也可以使用HorizontalScrollView来实现侧滑菜单.不过今天博主要说的是利用线性布局LinearLayout来实现,而且是水平方向上的线性布局. 可是LinearLayout作为水平展示时有点逗,因为如果下面有两个子视图的宽度都是match_parent,那么LinearLayout只会显示第一个子视图,第二个子视图却是怎么拉也死活显示不了.倘若在外侧加个H

Android开发笔记(一百三十二)矢量图形与矢量动画

矢量图形VectorDrawable 与水波图形RippleDrawable一样,矢量图形VectorDrawable也是Android5.0之后新增的图形类.矢量图不同于一般的图形,它是由一系列几何曲线构成的图像,这些曲线以数学上定义的坐标点连接而成.具体到实现上,则需开发者提供一个xml格式的矢量图形定义,然后系统根据矢量定义自动计算该图形的绘制区域.因为绘图结果是动态计算得到,所以不管缩放到多少比例,矢量图形都会一样的清晰,不像位图那样拉大后会变模糊. 矢量图形的xml定义有点复杂,其结构

Android开发笔记(一百零七)统计分析SDK

APP统计分析 用户画像 对程序员来说,用户画像就是用户的属性和行为:通俗地说,用户画像是包括了个人信息.兴趣爱好.日常行为等血肉丰满的客户实体.用户画像是精准营销的产物,企业通过收集用户的行为,然后分析出用户的特征与偏好,进而挖掘潜在的商业价值,实现企业效益的最大化. 用户画像的一个具体应用是电商app的"猜你喜欢"栏目,电商平台通过对用户购买过的商品进行统计,可以分析用户日常生活用的是什么物品:电商平台还可以对用户的搜索行为.浏览行为进行统计,从中分析用户感兴趣的商品,或者说考虑购

Android开发笔记(一百零四)消息推送SDK

推送的集成 常用概念 推送:从服务器把消息实时发到客户端app上,这就是推送,推送可用于发送系统通知.发送推荐信息.发送聊天消息等等. 别名:用于给移动设备取个好记的名字,比如电脑有计算机名,可以把别名理解为开发者给移送设备起的外号.不过,多个移动设备可以起一样的别名,这几个设备就会同时收到发给该别名的消息. 标记:用于给移动设备打标签,可以理解为分类,比如超市里的泰国大米既可以打上"粮食制品"的标签,也可以打上"进口商品"的标签.服务器可以统一给某个种类的移动设备