14. “我”模块(一)之“我”

上一节对视频模块进行了综述(可参见 10. 视频模块 进行了解),接下来将从“我”模块(一)开始详细介绍:

  • “我”模块(一)之创建数据库
  • “我”模块(一)之“我”
  • “我”模块(一)之注册
  • “我”模块(一)之登录
  • “我”模块(一)之个人资料
  • “我”模块(一)之个人资料修改

知识点

  • 掌握SQLite数据库的使用,能够使用数据库存储用户信息。
  • 掌握“我”界面开发,能够展示用户基本信息以及该界面的功能。
  • 掌握“登录”“注册”界面的开发,实现用户登录注册功能。
  • 掌握“个人资料”以及“修改”界面的开发,实现用户信息的展示与修改功能。

“我”

任务综述:
根据“我”界面设计可知,该节目包含了用户头像、用户名、日历、星座、涂鸦、地图、收藏以及设置。当用户处于登录状态时,点击用户头像会跳转到“个人资料”界面;当用户处于未登录状态时,点击用户头像会跳转到“登录”界面。

4. “我”界面

任务分析:
“我”界面需要显示头像、用户名、日历、星座、涂鸦、地图、收藏条目以及设置条目,界面效果如图所示。

14. “我”模块(一)之“我”_第1张图片
image.png

任务实施:
(1)创建“我”界面:fragment_me.xml。

(2)导入界面图片(9个)。

(3)添加circleimageview库。由于“我”界面中的头像用到了circleimageview库中的CircleImageView控件,因此需要添加circleimageview库到该项目中,在AS中选中项目/右击选择Open Module Settings/Dependencies/+/Library dependency,把de.hdodenhof:circleimageview:1.3.0库加入主项目中。

注意:
如果在Library dependency选项中找不到该库,则可以直接在该项目的build.gradle文件中添加如下代码。

   compile 'de.hdodenhof:circleimageview:1.3.0'

(4)添加cardview-v7库。由于“我”界面中需要显示类似卡片的样式,因此可以同上在Library dependency选项,然后找到com.android.support:cardview-v7库并添加到项目中。

(5)放置界面控件。
一个ImageView控件用于显示红色背景图片;
一个Toolbar控件用于显示上下滑动的标题栏;
一个CircleImageView控件用于显示头像;
在该布局文件中,通过标签将layout_content_me.xml(局部布局)引入。

fragment_me.xml



    
        
            
            
        
    
    
        
    
    
    

(6)放置layout_content_me.xml文件中的控件。
8个ImageView控件,其中4个分别用于显示日历图标、星座图标、涂鸦图标以及地图图标,2个用于显示收藏图标与设置图标,2个用于显示收藏与设置条目后面的箭头;
6个TextView控件,其中4个分别用于显示日历文本、星座文本、涂鸦文本以及地图文本;2个分别用于显示收藏与设置的文本;
4个View控件,其中3个用于分隔日历、星座、涂鸦、地图,1个用于分隔收藏和设置条目。

layout_content_me.xml



    
        
            
                
                
            
            
            
                
                
            
            
            
                
                
            
            
            
                
                
            
        
    
    
        
            
                
                
                
            
            
            
                
                
                
            
        
    

(7)修改colors.xml文件与dimens.xml文件。

    #999999
    #ebebeb
    #eb413d

需要注意的是,在colors.xml文件中默认会有colorPrimary颜色,仅修改其值即可。在res/values文件夹的dimens.xml文件中添加如下代码:

    25dp
    14sp
    120dp
    16dp
    40dp
    8dp
    56dp

(8)添加一个自定义AvatarBehavior。由于向上推动“我”界面时,头像会向上移动到屏幕左上角,向下拉动“我”界面时,头像会移动到它原来的位置,因此需要自定义一个AvatarBehavior类并继承CoordinatorLayout.Behavior类。

AvatarBehavior.java

/**
 * 头像Behavior
 */
public class AvatarBehavior extends CoordinatorLayout.Behavior {
    private static final float ANIM_CHANGE_POINT = 0.2f; //缩放动画变化的支点
    private Context mContext;
    private int mTotalScrollRange;  //整个滚动的范围
    private int mAppBarHeight;      //AppBarLayout高度
    private int mAppBarWidth;       //AppBarLayout宽度
    private int mOriginalSize;     //控件原始大小
    private int mFinalSize;        //控件最终大小
    private float mScaleSize;     //控件最终缩放的尺寸,设置坐标值需要算上该值
    private float mOriginalX;     //原始x坐标
    private float mFinalX;        //最终x坐标
    private float mOriginalY;    //起始y坐标
    private float mFinalY;        //最终y坐标
    private int mToolBarHeight; //ToolBar高度
    private float mAppBarStartY; //AppBar的起始Y坐标
    private float mPercent;       //滚动执行百分比[0~1]
    private DecelerateInterpolator mMoveYInterpolator;  //Y轴移动插值器
    private AccelerateInterpolator mMoveXInterpolator; //X轴移动插值器
    //最终变换的视图,因为在5.0以上AppBarLayout在收缩到最终状态会覆盖变换后的视图,
    //所以添加一个最终显示的图片
    private CircleImageView mFinalView;
    private int mFinalViewMarginBottom; //最终变换的视图离底部的大小
    public AvatarBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        mMoveYInterpolator = new DecelerateInterpolator();
        mMoveXInterpolator = new AccelerateInterpolator();
        if (attrs != null) {
            TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.
                    AvatarImageBehavior);
            mFinalSize = (int) a.getDimension(R.styleable.AvatarImageBehavior_finalSize,
                    0);
            mFinalX = a.getDimension(R.styleable.AvatarImageBehavior_finalX, 0);
            mToolBarHeight = (int) a.getDimension(R.styleable.
                    AvatarImageBehavior_toolBarHeight, 0);
            a.recycle();
        }
    }
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View
            dependency) {
        return dependency instanceof AppBarLayout;
    }
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView
            child, View dependency) {
        if (dependency instanceof AppBarLayout) {
            _initVariables(child, dependency);
            mPercent = (mAppBarStartY - dependency.getY()) * 1.0f / mTotalScrollRange;
            float percentY = mMoveYInterpolator.getInterpolation(mPercent);
            AnimHelper.setViewY(child, mOriginalY, mFinalY - mScaleSize, percentY);
            if (mPercent > ANIM_CHANGE_POINT) {
                float scalePercent = (mPercent - ANIM_CHANGE_POINT) / (1 -
                        ANIM_CHANGE_POINT);
                float percentX = mMoveXInterpolator.getInterpolation(scalePercent);
                AnimHelper.scaleView(child, mOriginalSize, mFinalSize, scalePercent);
                AnimHelper.setViewX(child, mOriginalX, mFinalX - mScaleSize, percentX);
            } else {
                AnimHelper.scaleView(child, mOriginalSize, mFinalSize, 0);
                AnimHelper.setViewX(child, mOriginalX, mFinalX - mScaleSize, 0);
            }
            if (mFinalView != null) {
                if (percentY == 1.0f) {
                    //滚动到顶时才显示
                    mFinalView.setVisibility(View.VISIBLE);
                } else {
                    mFinalView.setVisibility(View.GONE);
                }
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && dependency
                instanceof CollapsingToolbarLayout) {
            //大于5.0才生成新的最终的头像,因为5.0以上AppBarLayout会覆盖变换后的头像
            if (mFinalView == null && mFinalSize != 0 && mFinalX != 0 &&
                    mFinalViewMarginBottom != 0) {
                mFinalView = new CircleImageView(mContext);
                mFinalView.setVisibility(View.GONE);
                //添加为CollapsingToolbarLayout子视图
                ((CollapsingToolbarLayout) dependency).addView(mFinalView);
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mFinalView.
                        getLayoutParams();
                params.width = mFinalSize; //设置大小
                params.height = mFinalSize;
                params.gravity = Gravity.BOTTOM; //设置位置,最后显示时相当于变换后的头像位置
                params.leftMargin = (int) mFinalX;
                params.bottomMargin = mFinalViewMarginBottom;
                mFinalView.setLayoutParams(params);
                mFinalView.setImageDrawable(child.getDrawable());
                mFinalView.setBorderColor(child.getBorderColor());
                int borderWidth = (int) ((mFinalSize * 1.0f / mOriginalSize) *
                        child.getBorderWidth());
                mFinalView.setBorderWidth(borderWidth);
            } else {
                mFinalView.setImageDrawable(child.getDrawable());
                mFinalView.setBorderColor(child.getBorderColor());
                int borderWidth = (int) ((mFinalSize * 1.0f / mOriginalSize) *
                        child.getBorderWidth());
                mFinalView.setBorderWidth(borderWidth);
            }
        }
        return true;
    }
    /**
     * 初始化变量
     */
    private void _initVariables(CircleImageView child, View dependency) {
        if (mAppBarHeight == 0) {
            mAppBarHeight = dependency.getHeight();
            mAppBarStartY = dependency.getY();
        }
        if (mTotalScrollRange == 0) {
            mTotalScrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
        }
        if (mOriginalSize == 0) {
            mOriginalSize = child.getWidth();
        }
        if (mFinalSize == 0) {
            mFinalSize = mContext.getResources().getDimensionPixelSize(R.dimen.
                    avatar_final_size);
        }
        if (mAppBarWidth == 0) {
            mAppBarWidth = dependency.getWidth();
        }
        if (mOriginalX == 0) {
            mOriginalX = child.getX();
        }
        if (mFinalX == 0) {
            mFinalX = mContext.getResources().getDimensionPixelSize(R.dimen.
                    avatar_final_x);
        }
        if (mOriginalY == 0) {
            mOriginalY = child.getY();
        }
        if (mFinalY == 0) {
            if (mToolBarHeight == 0) {
                mToolBarHeight = mContext.getResources().getDimensionPixelSize(R.dimen.
                        toolbar_height);
            }
            mFinalY = (mToolBarHeight - mFinalSize) / 2 + mAppBarStartY;
        }
        if (mScaleSize == 0) {
            mScaleSize = (mOriginalSize - mFinalSize) * 1.0f / 2;
        }
        if (mFinalViewMarginBottom == 0) {
            mFinalViewMarginBottom = (mToolBarHeight - mFinalSize) / 2;
        }
    }
}

(9)添加一个attrs.xml文件。由于需要设置自定义控件AvatarBehavior中的属性值的类型,因此需要创建res/values/attrs.xml文件,该文件中的name表示属性的名称,format表示属性的类型。

attrs.xml



    
        
        
        
        
        
        
        
        
    

(10)创建AnimHelper。由于需要设置AvatarBehavior控件的X轴与Y轴的坐标,因此在utils包创建一个AnimHelper类,在该类中创建setViewX()与setViewY()方法,分别用于设置X轴与Y轴坐标。

AnimHelper.java

public class AnimHelper {
    private AnimHelper() {
        throw new RuntimeException("AnimHelper cannot be initialized!");
    }
    public static void setViewX(View view, float originalX, float finalX, float percent)
    {
        float calcX = (finalX - originalX) * percent + originalX;
        view.setX(calcX);
    }
    public static void setViewY(View view, float originalY, float finalY, float percent)
    {
        float calcY = (finalY - originalY) * percent + originalY;
        view.setY(calcY);
    }
    public static void scaleView(View view, float originalSize, float finalSize, float
            percent) {
        float calcSize = (finalSize - originalSize) * percent + originalSize;
        float caleScale = calcSize / originalSize;
        view.setScaleX(caleScale);
        view.setScaleY(caleScale);
    }
}

5. 广播接收者

任务分析:
由于在个人头像修改后,需要在“我”界面更新头像,因此需要创建一个广播快速更新头像。

任务实施:
创建receiver包,在包中创建UpdateUserInfoReceiver并继承BroadcastReceiver类,同时重写onReceive()方法。

UpdateUserInfoReceiver.java

public class UpdateUserInfoReceiver extends BroadcastReceiver {
    public interface ACTION {
        String UPDATE_USERINFO = "update_userinfo";
    }
    //广播intent类型
    public interface INTENT_TYPE {
        String TYPE_NAME = "intent_name";
        String UPDATE_HEAD = "update_head";//更新头像
    }
    private BaseOnReceiveMsgListener onReceiveMsgListener;
    public UpdateUserInfoReceiver(BaseOnReceiveMsgListener onReceiveMsgListener) {
        this.onReceiveMsgListener = onReceiveMsgListener;
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        onReceiveMsgListener.onReceiveMsg(context, intent);
    }
    public interface BaseOnReceiveMsgListener {
        void onReceiveMsg(Context context, Intent intent);
    }
}

6. “我”界面逻辑代码

任务分析:
在“我”界面中需要判断用户是否登录。若用户未登录,则界面上显示“点击登录”,并且点击头像会跳转到“登录”界面;若用户已经登录,则界面上显示用户名,并且点击头像会跳转到“个人资料”界面。点击“收藏”条目时跳转到“收藏”界面,点击“设置”条目时跳转到“设置”界面。

任务实施:
(1)创建MeFragment类并实现OnClickListener接口。

(2)获取界面控件。创建界面控件的初始化方法initView(),用于获取“我”界面上所要用到的控件,并通过readLoginStatus()方法判断当前是否为登录状态,如果是,则需要设置对应控件的状态。同时在此方法中还需要处理控件的点击事件。

(3)接收广播。由于个人资料界面在修改头像成功后需要及时更新“我”界面的头像,因此需要在MeFragment类中创建一个receiver()方法接收传递过来的头像信息并更新界面头像。

(4)设置“我”界面的登录成功状态。在MeFragment类中创建一个setLoginParams()方法,用于设置登陆成功后“我”界面中的头像与用户名的显示状态。

(5)回传数据。在MeFragment类中重写onActivityResult()方法,在该方法中接收从“登录”界面(暂未创建)或者“设置”界面(暂未创建)回传过来的登录状态,从而设置“我”界面。

public class MeFragment extends Fragment implements View.OnClickListener {
    private LinearLayout ll_calendar, ll_constellation, ll_scraw, ll_map;
    private RelativeLayout rl_collection, rl_setting;
    private CircleImageView iv_avatar;
    private View view;
    private UpdateUserInfoReceiver updateUserInfoReceiver;
    private IntentFilter filter;
    private boolean isLogin = false;
    private CollapsingToolbarLayout collapsingToolbarLayout;
    public MeFragment() {
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_me, container, false);
        initView(view);
        return view;
    }
    private void initView(View view) {
        ll_calendar = (LinearLayout) view.findViewById(R.id.ll_calendar);
        ll_constellation = (LinearLayout) view.findViewById(R.id.ll_constellation);
        ll_scraw = (LinearLayout) view.findViewById(R.id.ll_scraw);
        ll_map = (LinearLayout) view.findViewById(R.id.ll_map);
        rl_collection = (RelativeLayout) view.findViewById(R.id.rl_collection);
        rl_setting = (RelativeLayout) view.findViewById(R.id.rl_setting);
        iv_avatar = (CircleImageView) view.findViewById(R.id.iv_avatar);
        collapsingToolbarLayout = (CollapsingToolbarLayout) view.findViewById(R.id.
                collapsing_tool_bar);
        collapsingToolbarLayout.setExpandedTitleTextAppearance(R.style.ToolbarTitle);
        isLogin = UtilsHelper.readLoginStatus(getActivity());
        setLoginParams(isLogin);
        setListener();
        receiver();
    }
    private void receiver() {
        updateUserInfoReceiver = new UpdateUserInfoReceiver(
                new UpdateUserInfoReceiver.BaseOnReceiveMsgListener() {
                    @Override
                    public void onReceiveMsg(Context context, Intent intent) {
                        String action = intent.getAction();
                        if (UpdateUserInfoReceiver.ACTION.UPDATE_USERINFO
                                .equals(action)) {
                            String type = intent.getStringExtra(UpdateUserInfoReceiver.
                                    INTENT_TYPE.TYPE_NAME);
                            if (UpdateUserInfoReceiver.INTENT_TYPE.UPDATE_HEAD  //更新头像
                                    .equals(type)) {
                                String head = intent.getStringExtra("head");
                                Bitmap bt = BitmapFactory.decodeFile(head);
                                if (bt != null) {
                                    Drawable drawable = new BitmapDrawable(bt);
                                    iv_avatar.setImageDrawable(drawable);
                                } else {
                                    iv_avatar.setImageResource(R.drawable.default_head);
                                }
                            }
                        }
                    }
                });
        filter = new IntentFilter(UpdateUserInfoReceiver.ACTION.UPDATE_USERINFO);
        getActivity().registerReceiver(updateUserInfoReceiver, filter);
    }
    private void setListener() {
        ll_calendar.setOnClickListener(this);
        ll_constellation.setOnClickListener(this);
        ll_scraw.setOnClickListener(this);
        ll_map.setOnClickListener(this);
        rl_collection.setOnClickListener(this);
        rl_setting.setOnClickListener(this);
        iv_avatar.setOnClickListener(this);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (updateUserInfoReceiver != null) {
            getActivity().unregisterReceiver(updateUserInfoReceiver);
    }
    }
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.ll_calendar:
                Intent calendarIntent = new Intent(getActivity(), CalendarActivity.class);
                startActivity(calendarIntent);
                break;
            case R.id.ll_constellation:
                Intent constellIntent = new Intent(getActivity(), ConstellationActivity.class);
                startActivity(constellIntent);
                break;
            case R.id.ll_scraw:
                Intent scarwIntent = new Intent(getActivity(), ScrawActivity.class);
                startActivity(scarwIntent);
                break;
            case R.id.ll_map:
                Intent mapIntent = new Intent(getActivity(), MapActivity.class);
                startActivity(mapIntent);
                break;
            case R.id.rl_collection:
                if (isLogin) {
                    Intent collection = new Intent(getActivity(), CollectionActivity.class);
                    startActivity(collection);
                } else {
                    Toast.makeText(getActivity(), "您还未登录,请先登录",
                            Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.rl_setting:
                if (isLogin) {
                    //跳转到设置界面
                    Intent settingIntent = new Intent(getActivity(), SettingActivity.class);
                    startActivityForResult(settingIntent, 1);
                } else {
                    Toast.makeText(getActivity(), "您还未登录,请先登录",
                            Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.iv_avatar:
                if (isLogin) {
                    //跳转到个人资料界面
                    Intent userinfo = new Intent(getActivity(), UserInfoActivity.class);
                    startActivity(userinfo);
                } else {
                    //跳转到登录界面
                    Intent login = new Intent(getActivity(), LoginActivity.class);
                    startActivityForResult(login, 1);
                }
                break;
        }
    }
    /**
     * 登录成功后设置我的界面
     */
    public void setLoginParams(boolean isLogin) {
        if (isLogin) {
            String userName = UtilsHelper.readLoginUserName(getActivity());
            collapsingToolbarLayout.setTitle(userName);
            String head = DBUtils.getInstance(getActivity()).getUserHead(userName);
            Bitmap bt = BitmapFactory.decodeFile(head);
            if (bt != null) {
                Drawable drawable = new BitmapDrawable(bt);//转换成drawable
                iv_avatar.setImageDrawable(drawable);
            } else {
                iv_avatar.setImageResource(R.drawable.default_head);
            }
        } else {
            iv_avatar.setImageResource(R.drawable.default_head);
            collapsingToolbarLayout.setTitle("点击登录");
        }
    }
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data != null) {
            boolean isLogin = data.getBooleanExtra("isLogin", false);
            setLoginParams(isLogin);
            this.isLogin = isLogin;
        }
    }
}

(6)修改UtilsHelper.java文件。由于“我”界面需要根据登录状态设置相应的图标和控件的显示,因此需要在utils包中的UtilsHelper类中创建readLoginStatus()方法,从SharedPreferences中读取登录状态。

   /**
     * 从SharedPreferences中读取登录状态
     */
    public static boolean readLoginStatus(Context context){
        SharedPreferences sp=context.getSharedPreferences("loginInfo",Context.
                MODE_PRIVATE);
        boolean isLogin=sp.getBoolean("isLogin", false);
        return isLogin;
    }

由于在“我”界面中需要显示用户名,因此当用户处于登录状态时,需要在utils包中的UtilsHelper类中创建readLoginUserName()方法,从SharedPreferences中读取用户名。

    /**
     * 从SharedPreferences中读取登录用户名
     */
    public static String readLoginUserName(Context context) {
        SharedPreferences sp = context.getSharedPreferences("loginInfo",
                Context.MODE_PRIVATE);
        String userName = sp.getString("loginUserName", "");//读取登录时的用户名
        return userName;
    }

(7)修改DBUtils.java文件。由于“我”界面中需要根据用户名获取头像。

 /*
 * 根据登录名获取用户头像
 */
    public String getUserHead(String userName) {
        String sql = "SELECT head FROM " + SQLiteHelper.U_USERINFO + " WHERE userName=?";
        Cursor cursor = db.rawQuery(sql, new String[]{userName});
        String head = "";
        while (cursor.moveToNext()) {
            head = cursor.getString(cursor.getColumnIndex("head"));
        }
        cursor.close();
        return head;
    }

(8)修改styles.xml文件。“我”界面中需要设置标题字体的大小。

    
    

(9)修改底部导航栏。由于点击底部导航栏的“我”按钮时会出现“我”界面,因此需要找到MainActivity.java中的initView()方法,在其中添加:

MeFragment meFragment = new MeFragment();

在“alFragment.add(videoFragment);”语句下方添加:

alFragment.add(meFragment);
viewPager.setOffscreenPageLimit(3); //三个界面之间来回切换都不会重新加载数据。

你可能感兴趣的:(14. “我”模块(一)之“我”)