015-显示"全部理财"模块数据
1. 布局fragment_invest.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<include layout="@layout/common_title"></include>
<com.viewpagerindicator.TabPageIndicator
android:id="@+id/tabpage_invest"
android:layout_width="match_parent"
android:layout_height="wrap_content"></com.viewpagerindicator.TabPageIndicator>
<android.support.v4.view.ViewPager
android:id="@+id/vp_invest"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v4.view.ViewPager>
</LinearLayout>
2. 跑马灯效果的实现
方式一:
/**
* 使得当前的textView获取焦点
*/
private void settvProductTitleFocusable()
{
tvProductTitle.setFocusable(true);
// tvProductTitle.setFocusableInTouchMode(true);
// tvProductTitle.requestFocus();
}
方式二:
TextView只有在获取焦点后才会滚动显示隐藏文字,重写isFocused()方法,返回true,可设置跑马灯无限循环,一直“跑”
自定义继承于TextView的控件,并重写:
@Override
public boolean isFocused()
{
return true;
}
在布局中使用此控件。
3. ListView中每个item的数据解析
/**
* 使用fastJson解析json数据
*
* @param json
*/
private void processJson(String json)
{
JSONObject jsonObject = JSON.parseObject(json);
Boolean success = jsonObject.getBoolean("success");
if (success)
{
String data = jsonObject.getString("data");
//获取集合数据
productList = JSON.parseArray(data, Product.class);
//设置适配器
//方式一:没有抽取
// ProductAdapter productAdapter = new ProductAdapter(productList);
// lvProductList.setAdapter(productAdapter);//显示列表
//方式二:抽取了,但是抽取力度小 (可以作为选择)
// ProductAdapter1 productAdapter1 = new ProductAdapter1(productList);
// lvProductList.setAdapter(productAdapter1);//显示列表
//方式三:抽取了,但是没有使用ViewHolder,getView()优化的不够
// ProductAdapter2 productAdapter2 = new ProductAdapter2(productList);
// lvProductList.setAdapter(productAdapter2);//显示列表
//方式四:抽取了,最好的方式.(可以作为选择)
ProductAdapter3 productAdapter3 = new ProductAdapter3(productList);
lvProductList.setAdapter(productAdapter3);//显示列表
}
}
4. 如何在ListView中展示不同布局的item
//分类型的listView如下实现
//不同的position位置上,显示的具体的item的type值
@Override
public int getItemViewType(int position)
{
if (position == 3)
{
return 0;
}
else
{
return 1;
}
}
//返回不同类型的item的个数
@Override
public int getViewTypeCount()
{
return 2;
}
016-抽取得到公共的BaseAdapter
方式一:简单抽取:抽取共同的方法
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/21 16:11
* @function:listView抽取方式一
*/
public abstract class MyBaseAdapter1<T> extends BaseAdapter
{
public List<T> list;
//通过构造器初始化集合数据
public MyBaseAdapter1(List<T> list)
{
this.list = list;
}
@Override
public int getCount()
{
return list == null ? 0 : list.size();
}
@Override
public Object getItem(int position)
{
return list.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View view = myGetView(position,convertView,parent);
return view;
}
public abstract View myGetView(int position, View convertView, ViewGroup parent);
}
方式二:针对getView()的进一步抽取
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/21 16:24
* @function:抽取方式二
*/
public abstract class MyBaseAdapter2<T> extends BaseAdapter
{
public List<T> list;
//通过构造器初始化集合数据
public MyBaseAdapter2(List<T> list)
{
this.list = list;
}
@Override
public int getCount()
{
return list == null ? 0 : list.size();
}
@Override
public Object getItem(int position)
{
return list.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
//将具体的集合数据装配到具体的一个item layout中
//问题一:item layout的布局是不确定的。
//问题二:将集合中指定位置的数据装配到item,是不确定的。
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if (convertView == null)
{
convertView = initView(parent.getContext());
holder = new ViewHolder(convertView);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
//获取集合数据
T t = list.get(position);
//装配数据
setData(convertView, t);
return convertView;
}
//装配数据的操作
protected abstract void setData(View convertView, T t);
//提供具体的item layout的布局
protected abstract View initView(Context context);
class ViewHolder
{
public ViewHolder(View view)
{
view.setTag(this);
}
}
}
方式三:面向ViewHolder的抽取
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/21 16:26
* @function:抽取方式三holder的基类
*/
public abstract class BaseHolder<T>
{
private View rootView;
private T data;
public BaseHolder()
{
rootView = initItemLayout();
rootView.setTag(this);
ButterKnife.bind(this, rootView);
}
/**
* 提供listView item的具体布局
*
* @return
*/
protected abstract View initItemLayout();
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
refreshData();
}
//装配过程
protected abstract void refreshData();
public View getRootView()
{
return rootView;
}
}
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/21 16:32
* @function:抽取方式三
*/
public abstract class MyBaseAdapter3<T> extends BaseAdapter
{
public List<T> list;
//通过构造器初始化集合数据
public MyBaseAdapter3(List<T> list)
{
this.list = list;
}
@Override
public int getCount()
{
return list == null ? 0 : list.size();
}
@Override
public Object getItem(int position)
{
return list.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
/**
* 将具体的集合数据装配到具体的一个item layout中
//问题一:item layout的布局是不确定的。
//问题二:将集合中指定位置的数据装配到item,是不确定的。
* @param position
* @param convertView
* @param parent
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
BaseHolder<T>holder;
if(convertView==null)
{
holder=getHolder();
}
else
{
holder = (BaseHolder<T>) convertView.getTag();
}
//装配数据
T t=list.get(position);
holder.setData(t);
return holder.getRootView();
}
protected abstract BaseHolder<T> getHolder();
}
17-随机飞入效果的实现
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/20 18:10
* @function:推荐理财fragment 如何在布局中添加子视图?
* 1.直接在布局文件中,以标签的方式添加。---静态
* 2.在java代码中,动态的添加子视图:
* --->addView(...):一个一个的添加
* --->设置adapter的方式,批量装配数据。
*/
public class ProductRecommondFragment extends BaseFragment
{
@Bind(R.id.stellar_map)
StellarMap stellarMap;
//提供装配的数据
private String[] datas = new String[]{"新手福利计划", "财神道90天计划", "硅谷钱包计划", "30天理财计划(加息2%)", "180天理财计划(加息5%)", "月月升理财计划(加息10%)",
"中情局投资商业经营", "大学老师购买车辆", "屌丝下海经商计划", "美人鱼影视拍摄投资", "Android培训老师自己周转", "养猪场扩大经营",
"旅游公司扩大规模", "铁路局回款计划", "屌丝迎娶白富美计划"
};
//声明两个子数组
private String[] oneDatas = new String[datas.length / 2];
private String[] twoDatas = new String[datas.length - datas.length / 2];
private Random random = new Random();
@Override
protected RequestParams getParams()
{
return null;
}
@Override
protected String getUrl()
{
return null;
}
@Override
protected void initData(String content)
{
//初始化子数组的数据
for (int i = 0; i < datas.length; i++)
{
if (i < datas.length / 2)
{
oneDatas[i] = datas[i];
}
else
{
twoDatas[i - datas.length / 2] = datas[i];
}
}
StellarAdapter adapter = new StellarAdapter();
stellarMap.setAdapter(adapter);
//设置stellarMap的内边距
int leftPadding = UIUtils.dp2px(10);
int topPadding = UIUtils.dp2px(10);
int rightPadding = UIUtils.dp2px(10);
int bottomPadding = UIUtils.dp2px(10);
stellarMap.setInnerPadding(leftPadding, topPadding, rightPadding, bottomPadding);
//必须调用如下的两个方法,否则stellarMap不能显示数据
//设置显示的数据在x轴、y轴方向上的稀疏度
stellarMap.setRegularity(5, 7);
//设置初始化显示的组别,以及是否需要使用动画
stellarMap.setGroup(0, true);
}
@Override
protected void initTitle()
{
}
@Override
public int getLayoutId()
{
return R.layout.fragment_productrecommond;
}
//提供Adapter的实现类
class StellarAdapter implements StellarMap.Adapter
{
/**
* 获取组的个数
*
* @return
*/
@Override
public int getGroupCount()
{
return 2;//这里一共两组
}
/**
* 返回每组中显示的数据的个数
*
* @param group
* @return
*/
@Override
public int getCount(int group)
{
if (group == 0)
{
return datas.length / 2;
}
else
{
return datas.length - datas.length / 2;
}
}
/**
* 返回具体的view.
* position:不同的组别,position都是从0开始。
*
* @param group
* @param position
* @param convertView
* @return
*/
@Override
public View getView(int group, int position, View convertView)
{
final TextView textView = new TextView(getActivity());
//设置属性
//设置文本的内容
if (group == 0)
{
textView.setText(oneDatas[position]);
}
else
{
textView.setText(twoDatas[position]);
}
//设置字体的大小
textView.setTextSize(UIUtils.dp2px(5) + UIUtils.dp2px(random.nextInt(10)));
//设置字体的颜色
int red = random.nextInt(200);//00 ~ ff ; 0 ~ 255
int green = random.nextInt(200);
int blue = random.nextInt(200);
textView.setTextColor(Color.rgb(red, green, blue));
//设置TextView的点击事件
textView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
UIUtils.toast(textView.getText().toString(), false);
}
});
return textView;
}
/**
* 返回下一组显示平移动画的组别。查看源码发现,此方法从未被调用。所以可以不重写
*
* @param group
* @param degree
* @return
*/
@Override
public int getNextGroupOnPan(int group, float degree)
{
return 0;
}
/**
* 返回下一组显示缩放动画的组别。
*
* @param group
* @param isZoomIn
* @return
*/
@Override
public int getNextGroupOnZoom(int group, boolean isZoomIn)
{
//来回切换组别
if (group == 0)
{
return 1;
}
else
return 0;
}
}
}
18-流式布局的使用
1-流式布局分析
1.流式布局特点描述:
在布局内,随意摆放任意个view,每行所摆放的view个数,根据实时计算出来的宽度,一旦当前要摆放的view宽度和之前摆放的所有view宽度加在一起,超过了布局的宽度,那么就把该view换行摆放。
2.应用场景:
一般,像这种流式布局会应用在一些热门标签,热门推荐之类的应用上
3.测量模式:
MeasureSpec.EXACTLY:精确模式, eg:100dp,match_parent.
MeasureSpec.AT_MOST: 至多模式, view最多可以获得的宽高值,它需要计算所有包含的子view的宽高,最后计算出来的宽高总和值,eg:wrap_content.
UNSPECIFIED:未指定模式,想设置多宽多高,就给你多宽多高,一般的控件不会指定这种模式,但也存在,eg:scrollview的宽高测量,就是使用的此种模式
在我们的流式布局内,应该怎么设置布局的宽高呢? onMeasure()
1:如果布局指定的宽是match_parent或者精确的宽度值,那么直接就可以从父控件传入的测量规格中直接获取布局宽度,高度同理.
2:如果布局指定的宽高不是EXACTLY,而是AT_MOST,那么这时候,就需要计算每一个子view的宽高,来决定布局的宽高了。
宽度:摆放的所有子view占据宽度最多的一行,作为布局宽度。
高度:摆放的所有子view总共占据几行的高度总和。
4.子View的布局方式: onLayout()
使用onLayout():设置ViewGroup内包含的所有子view的位置;
获取到每一行的每一个子view,计算出它的left,top,right,bottom,调用layout方法设置其在流式布局当中的位置。
宽度=子view占据宽度最多的那行的宽度=那一行每一个子view的宽度+leftMargin+rightMargin;
高度=所有行的高度 = 每一行的高度+topMargin+bottomMargin;
setMeasureDimension()--->设置流式布局的宽高。
2-流式布局的实现
//调用如下方法,才允许获取子视图的测量宽高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
//FlowLayout中有了如下的方法,在onMeasure()中可通过child就可以getLayoutParams(),返回MarginLayoutParams类对象,进而计算margin的值
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
MarginLayoutParams mp = new MarginLayoutParams(getContext(), attrs);
return mp;
}
3-动态的添加数据
private String[] datas = new String[]{"新手福利计划", "财神道90天计划", "硅谷计划", "30天理财计划", "180天理财计划", "月月升","中情局投资商业经营", "大学老师购买车辆", "屌丝下海经商计划", "美人鱼影视拍摄投资", "Android培训老师自己周转", "养猪场扩大经营",
"旅游公司扩大规模", "摩托罗拉洗钱计划", "铁路局回款计划", "屌丝迎娶白富美计划"
};
添加文本视图到布局中:
/**
* 加载流式布局
*/
private void loadFlowLayout()
{
for (int i = 0; i < datas.length; i++)
{
final TextView tv = new TextView(getContext());
//设置属性
tv.setText(datas[i]);
ViewGroup.MarginLayoutParams mp = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mp.leftMargin = UIUtils.dp2px(5);
mp.rightMargin = UIUtils.dp2px(5);
mp.topMargin = UIUtils.dp2px(5);
mp.bottomMargin = UIUtils.dp2px(5);
tv.setLayoutParams(mp);//设置边距
int padding = UIUtils.dp2px(5);
tv.setPadding(padding, padding, padding, padding);//设置内边距
tv.setTextSize(UIUtils.dp2px(10));
Random random = new Random();
int red = random.nextInt(211);
int green = random.nextInt(211);
int blue = random.nextInt(211);
//设置单一背景
// tv.setBackground(DrawUtils.getDrawable(Color.rgb(red,green,blue),UIUtils.dp2px(5)));
//设置具有选择器功能的背景
tv.setBackground(DrawUtils.getSelector(DrawUtils.getDrawable(Color.rgb(red, green, blue), UIUtils.dp2px(5)), DrawUtils.getDrawable(Color.WHITE, UIUtils.dp2px(5))));
//设置textView是可点击的.如果设置了点击事件,则TextView就是可点击的。
// tv.setClickable(true);
tv.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
UIUtils.toast(tv.getText().toString(), false);
}
});
flowHot.addView(tv);
}
}
4-GradientDrawable的使用
官方解释:
它是可以代替定义shape的xml资源的drawable,可以使用在一个button按钮的gradient渐变颜色属性上,或者backgrounds等。
使用代码定义的shape资源类
关键api方法:
setColor(rgb); //填充颜色
setGradientType(GradientDrawable.RECTANGLE); //shape矩形
setCornerRadius(radius); //四周圆角半径
setStroke(1,strokenColor); //边框厚度与颜色
5-设置颜色改变和点击事件
//设置单一背景
// tv.setBackground(DrawUtils.getDrawable(Color.rgb(red,green,blue),UIUtils.dp2px(5)));
//设置具有选择器功能的背景
tv.setBackground(DrawUtils.getSelector(DrawUtils.getDrawable(Color.rgb(red, green, blue), UIUtils.dp2px(5)), DrawUtils.getDrawable(Color.WHITE, UIUtils.dp2px(5))));
//设置textView是可点击的.如果设置了点击事件,则TextView就是可点击的。
// tv.setClickable(true);
tv.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
UIUtils.toast(tv.getText().toString(), false);
}
});
019-登录功能
1-MeFragment布局的设置
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/common_title" />
<com.example.p2pinvest.ui.MyScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_me"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="@color/my_title_bg"
android:gravity="center">
<RelativeLayout
android:id="@+id/rl_me_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_centerHorizontal="true">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="fitXY"
android:src="@drawable/my_user_bg_icon" />
<ImageView
android:id="@+id/iv_me_icon"
android:layout_width="62dp"
android:layout_height="62dp"
android:layout_centerInParent="true"
android:scaleType="fitXY"
android:src="@drawable/my_user_default" />
</RelativeLayout>
<TextView
android:id="@+id/tv_me_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/rl_me_icon"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="Hi,welcome!"
android:textColor="@color/white"
android:textSize="14sp" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="学习提示:"
android:textColor="@android:color/holo_red_dark" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="充值我们将以集成支付宝sdk支付金额演示充值功能" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="提现业务:在p2p金融平台中,提现都不是实时到账的,而是审核提现,系统将会告知24小时内进行转账,如果未收到,请联系客服..." />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center">
<ImageView
android:id="@+id/recharge"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="9dp"
android:layout_weight="1"
android:src="@drawable/my_recharge_bg" />
<ImageView
android:id="@+id/withdraw"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="9dp"
android:layout_weight="1"
android:src="@drawable/my_withdraw_bg" />
</LinearLayout>
<!--充分使用textview属性代替LinearLayout布局-->
<TextView
android:id="@+id/ll_touzi"
style="@style/LoginStyle"
android:drawableLeft="@drawable/my_invest_manage"
android:text="投资管理" />
<TextView
android:id="@+id/ll_touzi_zhiguan"
style="@style/LoginStyle"
android:drawableLeft="@drawable/my_reward_manage"
android:text="投资管理(直观)" />
<TextView
android:id="@+id/ll_zichan"
style="@style/LoginStyle"
android:drawableLeft="@drawable/my_asset_manage"
android:text="资产管理" />
</LinearLayout>
</com.example.p2pinvest.ui.MyScrollView>
</LinearLayout>
2-验证本地是否有已登录信息
1.验证是否已经登录
/**
* 判断用户是否已经登录
*/
private void isLogin()
{
//查看本地是否有用户的登录信息
SharedPreferences sp = this.getActivity().getSharedPreferences("user_info", Context.MODE_PRIVATE);
String name = sp.getString("name", "");
if (TextUtils.isEmpty(name))
{
//本地没有保存过用户信息,给出提示:登录
doLogin();
}
else
{
//已经登录过,则直接加载用户的信息并显示
loadUserInfo();
}
}
2.登录提示:
3.登录页面:(根据是否本地保存有用户登录信息,决定是否打开登录界面:LoginActivity)
3-BaseActivity的抽取
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/22 19:36
* @function:Activity抽取
*/
public abstract class BaseActivity extends FragmentActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
ButterKnife.bind(this);
//将当前的activity添加到ActivityManager中
ActivityManager.getInstance().add(this);
initTitle();
initData();
}
/**
* 暴露给子类的抽象方法
*/
protected abstract void initData();
protected abstract void initTitle();
protected abstract int getLayoutId();
public AsyncHttpClient client = new AsyncHttpClient();
/**
* 启动新的activity
*
* @param Activity
* @param bundle
*/
public void goToActivity(Class Activity, Bundle bundle)
{
Intent intent = new Intent(this, Activity);
//携带数据
if (bundle != null && bundle.size() != 0)
{
intent.putExtra("data", bundle);
}
startActivity(intent);
}
/**
* 销毁当前的Activity
*
* @param acivity
*/
public void removeCurrentActivity(Activity acivity)
{
ActivityManager.getInstance().removeCurrentActivity();
}
/**
* 销毁所有的activity
*/
public void removeAllActivity()
{
ActivityManager.getInstance().removeAll();
}
/**
* 保存用户信息
*
* @param user
*/
public void saveUserInfo(User user)
{
SharedPreferences sp = this.getSharedPreferences("user_info", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("name", user.getName());
editor.putString("imageurl", user.getImageurl());
editor.putBoolean("iscredit", user.isCredit());
editor.putString("phone", user.getPhone());
editor.commit();//必须提交,否则保存不成功
}
/**
* 读取用户信息
*
* @return
*/
public User readUserInfo()
{
SharedPreferences sp = this.getSharedPreferences("user_info", Context.MODE_PRIVATE);
User user = new User();
user.setName(sp.getString("name", ""));
user.setImageurl(sp.getString("imageurl", ""));
user.setPhone(sp.getString("phone", ""));
user.setCredit(sp.getBoolean("iscredit", false));
return user;
}
}
4-LoginActivity的实现
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/22 19:36
* @function:登录页面
*/
public class LoginActivity extends BaseActivity
{
@Bind(R.id.iv_title_back)
ImageView ivTitleBack;
@Bind(R.id.tv_title)
TextView tvTitle;
@Bind(R.id.iv_title_setting)
ImageView ivTitleSetting;
@Bind(R.id.tv_login_number)
TextView tvLoginNumber;
@Bind(R.id.et_login_number)
EditText etLoginNumber;
@Bind(R.id.rl_login)
RelativeLayout rlLogin;
@Bind(R.id.tv_login_pwd)
TextView tvLoginPwd;
@Bind(R.id.et_login_pwd)
EditText etLoginPwd;
@Bind(R.id.btn_login)
Button btnLogin;
/**
* 返回键点击事件
*
* @param view
*/
@OnClick(R.id.iv_title_back)
public void back(View view)
{
removeAllActivity();
goToActivity(MainActivity.class, null);
}
@Override
protected void initData()
{
}
@Override
protected void initTitle()
{
//不能使用View.GONE,布局会塌陷
ivTitleBack.setVisibility(View.VISIBLE);
tvTitle.setText("用户登录");
ivTitleSetting.setVisibility(View.INVISIBLE);
}
@Override
protected int getLayoutId()
{
return R.layout.activity_login;
}
/**
* 登录按钮点击事件
*
* @param view
*/
@OnClick(R.id.btn_login)
public void login(View view)
{
String number = etLoginNumber.getText().toString().trim();
String pwd = etLoginPwd.getText().toString().trim();
if (!TextUtils.isEmpty(number) && !TextUtils.isEmpty(pwd))
{
String url = AppNetConfig.LOGIN;
RequestParams params = new RequestParams();
params.put("phone", number);
params.put("password", MD5Utils.MD5(pwd));
client.post(url, params, new AsyncHttpResponseHandler()
{
@Override
public void onSuccess(String content)
{//200 404
//解析json
JSONObject jsonObject = JSON.parseObject(content);
boolean success = jsonObject.getBoolean("success");
if (success)
{
//解析json数据,生成User对象
String data = jsonObject.getString("data");
User user = JSON.parseObject(data, User.class);
//保存用户信息
saveUserInfo(user);
//重新加载界面
removeAllActivity();
goToActivity(MainActivity.class, null);
}
else
{
Toast.makeText(LoginActivity.this, "用户名不存在或密码不正确", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Throwable error, String content)
{
UIUtils.toast("联网失败", false);
}
});
}
else
{
UIUtils.toast("用户名或密码不能为空", false);
}
}
}
020-MeFragment中读取已登录用户信息
/**
* 加载用户的信息并显示
*/
private void loadUserInfo()
{
//1.读取本地保存的用户信息
User user = ((BaseActivity) this.getActivity()).readUserInfo();
//2.获取对象信息,并设置给相应的视图显示
tvMeName.setText(user.getName());
//判断本地是否已经保存头像的图片,如果有,则不再执行联网操作
boolean isExist = readImage();
if (isExist)
{
return;
}
//使用Picasso联网获取图片
Picasso.with(this.getActivity())
.load(user.getImageurl())
.transform(new Transformation()
{
@Override
public Bitmap transform(Bitmap source)
{
//下载以后的内存中的bitmap对象
//压缩处理
Bitmap bitmap = BitmapUtils.zoom(source, UIUtils.dp2px(62), UIUtils.dp2px(62));
//圆形处理
bitmap = BitmapUtils.circleBitmap(bitmap);
//回收bitmap资源
source.recycle();
return bitmap;
}
@Override
public String key()
{
return "";//需要保证返回值不能为null。否则报错
}
})
.into(ivMeIcon);
}
21-用户头像的圆形显示
1.设置布局属性:
<ImageView
android:scaleType="fitXY"/>
2.得到指定圆形的Bitmap对象
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/22 19:36
* @function:图片工具类
*/
public class BitmapUtils
{
/**
* 代码:功能性代码;非功能性代码。
*
* @param source
* @return
*/
public static Bitmap circleBitmap(Bitmap source)
{
//获取Bitmap的宽度
int width = source.getWidth();
//以Bitmap的宽度值作为新的bitmap的宽高值。
Bitmap bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
//以此bitmap为基准,创建一个画布
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
//在画布上画一个圆
canvas.drawCircle(width / 2, width / 2, width / 2, paint);
//设置图片相交情况下的处理方式
//setXfermode:设置当绘制的图像出现相交情况时候的处理方式的,它包含的常用模式有:
//PorterDuff.Mode.SRC_IN 取两层图像交集部分,只显示上层图像
//PorterDuff.Mode.DST_IN 取两层图像交集部分,只显示下层图像
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//在画布上绘制bitmap
canvas.drawBitmap(source, 0, 0, paint);
return bitmap;
}
//实现图片的压缩处理
//设置宽高必须使用浮点型,否则导致压缩的比例:0
public static Bitmap zoom(Bitmap source, float width, float height)
{
Matrix matrix = new Matrix();
//图片的压缩处理
matrix.postScale(width / source.getWidth(), height / source.getHeight());
Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, false);
return bitmap;
}
}
3.处理后显示
//使用Picasso联网获取图片
Picasso.with(this.getActivity())
.load(user.getImageurl())
.transform(new Transformation()
{
@Override
public Bitmap transform(Bitmap source)
{
//下载以后的内存中的bitmap对象
//压缩处理
Bitmap bitmap = BitmapUtils.zoom(source, UIUtils.dp2px(62), UIUtils.dp2px(62));
//圆形处理
bitmap = BitmapUtils.circleBitmap(bitmap);
//回收bitmap资源
source.recycle();
return bitmap;
}
@Override
public String key()
{
return "";//需要保证返回值不能为null。否则报错
}
})
.into(ivMeIcon);
4.压缩图片:
//实现图片的压缩处理
//设置宽高必须使用浮点型,否则导致压缩的比例:0
public static Bitmap zoom(Bitmap source, float width, float height)
{
Matrix matrix = new Matrix();
//图片的压缩处理
matrix.postScale(width / source.getWidth(), height / source.getHeight());
Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, false);
return bitmap;
}
022-调用系统拍照或图库选择图片
1.拍照
//打开系统拍照程序,选择拍照图片
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(camera, CAMERA);
接着,解析得到图片数据,并设置给ImageView显示,同时保存到本地
//拍照
Bundle bundle = data.getExtras();
// 获取相机返回的数据,并转换为图片格式
Bitmap bitmap = (Bitmap) bundle.get("data");
//bitmap圆形裁剪
bitmap = BitmapUtils.zoom(bitmap, UIUtils.dp2px(62), UIUtils.dp2px(62));
Bitmap circleImage = BitmapUtils.circleBitmap(bitmap);
//真是项目当中,是需要上传到服务器的..这步我们就不做了。
//加载显示
iv.setImageBitmap(circleImage);
//将图片保存在本地
saveImage(circleImage);
2.图库
//打开系统图库程序,选择图片
Intent picture = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(picture, PICTURE);
1.解析图库的操作,跟android系统有很大相关性。不同的系统使用uri的authority有很大不同。
//android各个不同的系统版本,对于获取外部存储上的资源,返回的Uri对象都可能各不一样,
// 所以要保证无论是哪个系统版本都能正确获取到图片资源的话就需要针对各种情况进行一个处理了
//这里返回的uri情况就有点多了
//在4.4.2之前返回的uri是:content://media/external/images/media/3951或者file://....
// 在4.4.2返回的是content://com.android.providers.media.documents/document/image
2.根据系统相册选择的文件获取路径
/**
* 根据系统相册选择的文件获取路径
*
* @param uri
*/
@SuppressLint("NewApi")
private String getPath(Uri uri)
{
int sdkVersion = Build.VERSION.SDK_INT;
//高于4.4.2的版本
if (sdkVersion >= 19)
{
Log.e("TAG", "uri auth: " + uri.getAuthority());
if (isExternalStorageDocument(uri))
{
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
if ("primary".equalsIgnoreCase(type))
{
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
else if (isDownloadsDocument(uri))
{
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(this, contentUri, null, null);
}
else if (isMediaDocument(uri))
{
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type))
{
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
else if ("video".equals(type))
{
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}
else if ("audio".equals(type))
{
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return getDataColumn(this, contentUri, selection, selectionArgs);
}
else if (isMedia(uri))
{
String[] proj = {MediaStore.Images.Media.DATA};
Cursor actualimagecursor = this.managedQuery(uri, proj, null, null, null);
int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
actualimagecursor.moveToFirst();
return actualimagecursor.getString(actual_image_column_index);
}
}
else if ("content".equalsIgnoreCase(uri.getScheme()))
{
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(this, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme()))
{
return uri.getPath();
}
return null;
}
3.uri路径查询字段
/**
* uri路径查询字段
*
* @param context
* @param uri
* @param selection
* @param selectionArgs
* @return
*/
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)
{
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try
{
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst())
{
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
}
finally
{
if (cursor != null)
cursor.close();
}
return null;
}
private boolean isExternalStorageDocument(Uri uri)
{
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri)
{
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri)
{
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static boolean isMedia(Uri uri)
{
return "media".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri)
{
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
3.保存选择的头像数据
修改后的头像,正常情况下是需要上传到服务器更新服务器中的用户头像信息的。因为服务器的数据是最靠谱的。(补充:本地数据往往是在与服务器连接不成功的时候,才选择使用本地数据提供显示)
复习1:数据存储
复习2:如何保存Bitmap
//需求1: 加载存储空间中的图片资源并显示
Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory() + "/beauty.png");
//需求2: 将一个bitmap对象存储到data/data/应用包名/files/1.png
bitmap.compress(Bitmap.CompressFormat.PNG, 100, openFileOutput("1.png", Context.MODE_PRIVATE));
/**如何将内存中的Bitmap对象持久化到本地存储中:sp \ 手机内部、手机外部 \数据库 \网络
* 将Bitmap保存到本地的操作
* 数据的存储。(5种)
* Bimap:内存层面的图片对象。
* <p>
* 存储--->内存:
* BitmapFactory.decodeFile(String filePath);
* BitmapFactory.decodeStream(InputStream is);
* 内存--->存储:
* bitmap.compress(Bitmap.CompressFormat.PNG,100,OutputStream os);
*/
private void saveImage(Bitmap bitmap)
{
File filesDir;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{//判断sd卡是否挂载
//路径1:storage/sdcard/Android/data/包名/files
filesDir = this.getExternalFilesDir("");
}
else
{//手机内部存储
//路径:data/data/包名/files
filesDir = this.getFilesDir();
}
FileOutputStream fos = null;
try
{
File file = new File(filesDir, "icon.png");
fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
finally
{
if (fos != null)
{
try
{
fos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
023-返回功能
1.销毁当前activity
@OnClick(R.id.iv_top_back)
//返回按钮的退出操作
@OnClick(R.id.iv_top_back)
public void back(View view) {
ActivityManager.getInstance().removeCurrent();
}
2.重写MeFragment的onResume()
在MeFragment的回调方法中提供:
/**
* 当当前的Fragment显示时,考虑是否需要从本地读取用户头像
*/
@Override
public void onResume() {
super.onResume();
Log.e("TAG", "onResume");
readImage();
}
/**
* @return
*/
private boolean readImage()
{
File filesDir;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{//判断sd卡是否挂载
//路径1:storage/sdcard/Android/data/包名/files
filesDir = this.getActivity().getExternalFilesDir("");
}
else
{//手机内部存储
//路径:data/data/包名/files
filesDir = this.getActivity().getFilesDir();
}
File file = new File(filesDir, "icon.png");
if (file.exists())
{
//存储--->内存
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
ivMeIcon.setImageBitmap(bitmap);
return true;
}
return false;
}
3.MeFragment的读取登录数据方法中补充操作
经测试,还需要:在doUser()中添加如下代码:
//加载用户信息并显示
private void doUser() {
//1.读取本地保存的用户信息
User user = ((BaseActivity) this.getActivity()).readUser();
//2.获取对象信息,并设置给相应的视图显示。
tvMeName.setText(user.getName());
if(readImage()){
return;
}
Picasso.with(this.getActivity())
024-退出登录功能
/**
* "退出登录"button的回调方法
*/
@OnClick(R.id.btn_user_logout)
public void onBtnUserLogoutClicked(View view)
{
//1.将保存在sp中的数据清除
SharedPreferences sp = this.getSharedPreferences("user_info", Context.MODE_PRIVATE);
sp.edit().clear().commit();//清除数据操作必须提交;提交以后,文件仍存在,只是文件中的数据被清除了
//2.将本地保存的图片的file删除
File filesDir;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{//判断sd卡是否挂载
//路径1:storage/sdcard/Android/data/包名/files
filesDir = this.getExternalFilesDir("");
}
else
{//手机内部存储
//路径:data/data/包名/files
filesDir = this.getFilesDir();
}
File file = new File(filesDir, "icon.png");
if (file.exists())
{
file.delete();//删除存储中的文件
}
//3.销毁所有的activity
this.removeAllActivity();
//4.重新进入首页面
this.goToActivity(MainActivity.class, null);
}
网友评论