一.开发前的准备
1.创建一个MyApplication存放全局变量
public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();}}
2.创建一个BaseActivity做为全部Activity的父类
public class BaseActivity extends Activity {}
3.设置顶部状态栏颜色,在Values-colors.mxl添加颜色
<color name="maincolor">#d34343</color>
4.修改Values-system.xml文件的状态栏颜色,设置为上面预设值好颜色
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimaryDark">@color/maincolor</item> </style>
使用colorPrimaryDark修改状态栏颜色优先级会低于android:statusBarColor,同时设置2个属性会优先使用android:statusBarColor,但是android:statusBarColor只支持安卓5.0以上,如果在默认的Values-system.xml设置会报错如下
所以使用这个属性,必须添加一个Values-v21文件夹,创建system.xml文件,这样就不会报错了,5.0以上系统会直接读取这个样式文件
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimaryDark">@color/maincolor</item> </style>
设置完状态栏
二、设置启动页
创建启动页Activity(welcomeActivity),设置布局
<?xml version="1.0" encoding="utf-8"?><FrameLayout 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" tools:context=".acitivitys.welcomeActivity" android:background="@color/maincolor"> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center" android:background="@drawable/logo"/> </FrameLayout>
修改清单文件AndroidManifest.xml
修改默认启动页为welcomeActivity
<activity android:name=".acitivitys.welcomeActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".acitivitys.MainActivity"></activity>
三、登录功能
1.启动页之后停留3秒钟跳转到登录页面,创建登录页(LoginActivity)
2.创建对应布局文件(activity_login)
3.启动页添加跳转逻辑
3.1.声明使用Timer停留三秒钟
private Timer mTimer;
3.2.创建并在onCreate调用初始化方法init,初始化方法中包含Intent跳转
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_welcome); init(); } //初始化 public void init(){ mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { toLogin(); } },3*1000); } //跳转登录页面 public void toLogin(){ Intent intent = new Intent(this,LoginActivity.class); startActivity(intent); finish(); }
4.清单文件AndroidManifest添加上Activity
</application> <activity android:name=".acitivitys.LoginActivity"/> </application>
5.通用布局UI配置
5.1创建通用尺寸文件dimen,定义通用的尺寸,全局调用
<?xml version="1.0" encoding="utf-8"?><resources> <dimen name="navBarHeight">56dp</dimen> <dimen name="marginSize">16dp</dimen> <dimen name="navBarTitleSize">22dp</dimen></resources>
5.1创建通用标题栏布局nav_bar
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/navBarHeight" android:paddingRight="@dimen/marginSize" android:paddingLeft="@dimen/marginSize" android:background="@color/maincolor"> <ImageView android:id="@+id/IvBack" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/back" android:layout_gravity="center_vertical"/> <TextView android:id="@+id/TvTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="@dimen/navBarTitleSize" android:layout_gravity="center" android:textColor="@color/white" android:text="音乐"/> <ImageView android:id="@+id/IvMe" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/nickname" android:layout_gravity="center_vertical|right"/></FrameLayout>
5.3封装一个findViewById,方便调用,调用只需要使用fd();
//简化封装findVieId protected <T extends View> T fd(@IdRes int id){ return findViewById(id); }
5.4在BaseActivity配置初始化方法,控制标题栏是否显示返回和个人中心,并且实现点击返回监听事件
private ImageView mIvBack,mIvMe; private TextView mTvTitle; //初始化NavigationBar protected void initNavBar(boolean isShowBack,String title,boolean isShowMe){ mIvBack = fd(R.id.IvBack); mIvMe = fd(R.id.IvMe); mTvTitle = fd(R.id.TvTitle); //判断为True显示 否则隐藏 mIvBack.setVisibility(isShowBack ? View.VISIBLE : View.GONE); mIvMe.setVisibility(isShowMe ? View.VISIBLE : View.GONE); mTvTitle.setText(title); //返回 mIvBack.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); }
6.登录页面加载标题栏布局,调用initNavBar隐藏后退和个人中心
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); initView(); } public void initView(){ initNavBar(false,"登录",false); }
}
7.接下来正式操刀登录界面
7.1 新建一个views文件夹存放自定义View
7.2新建一个类,继承FragmeLayout,并实现构造方法
/** * 包名:com.example.musicdemo.activitys * 用户:Administrator * 时间:13:38 2019-10-25 * 描述:自定义布局,输入框 */public class InputLayout extends FrameLayout { public InputLayout(Context context) { super(context); } public InputLayout(Context context, AttributeSet attrs) { super(context, attrs); } public InputLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } //声明支持L以上 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public InputLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
7.3新建一个资源文件,用来定义输入框的属性
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="inputView"> <!--输入框前面小图标--> <attr name="input_icon" format="reference"></attr> <!--输入框文字--> <attr name="input_hint" format="string"></attr> <!--是否密文展示--> <attr name="is_password" format="boolean"></attr> </declare-styleable> </resources>
7.4创建一个布局文件
<?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="@dimen/inputView_height" android:orientation="horizontal" android:gravity="center_vertical" android:paddingLeft="@dimen/marginSize" android:paddingRight="@dimen/marginSize"> <ImageView android:id="@+id/iv_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingRight="@dimen/marginSize" android:src="@mipmap/phone"/> <EditText android:id="@+id/ed_input" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@null" android:hint="用户名"/></LinearLayout>
7.5回到自定义VIew的Layout(InputLayout),完成属性的绑定和布局的绑定
7.5.1声明控件和属性
//输入框前面小图标 private int inputIcon; //输入框提示文字 private String inputHint; //输入框是否密文显示 private boolean isPassword; //声明控件 private View mView; private ImageView mIv_icon; private EditText mEd_input;
7.5.2创建初始化方法,设置布局,绑定布局
public void init(Context context,AttributeSet attrs){ //加载样式文件 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.inputView); //输入框前面小图标,没有则使用默认logo inputIcon = typedArray.getResourceId(R.styleable.inputView_input_icon,R.mipmap.logo); //提示字 inputHint = typedArray.getString(R.styleable.inputView_input_hint); //是否密文显示 isPassword = typedArray.getBoolean(R.styleable.inputView_is_password,false); //释放 typedArray.recycle(); //绑定布局 mView = LayoutInflater.from(context).inflate(R.layout.input_view,this,false); mIv_icon = mView.findViewById(R.id.iv_icon); mEd_input = mView.findViewById(R.id.ed_input); //关联布局属性 mIv_icon.setImageResource(inputIcon); mEd_input.setHint(inputHint); mEd_input.setInputType(isPassword? InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD : InputType.TYPE_CLASS_PHONE); addView(mView); }
7.5.3在自定义View的构造方便的加上这个自定义布局类
public InputLayout(Context context) { super(context); init(context,null); } public InputLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context,attrs); } public InputLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context,attrs); } //声明支持L以上 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public InputLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context,attrs); }
7.5.4 最后再加上一个获取input的内容
//返回输入内容 public String getInputStr(){ return mEd_input.getText().toString().trim(); }
以上自定义View就已经完成了!
7.6配置登录页面的整体布局,编辑activity_logo布局
* 引用自定义布局需要使用命名空间app
xmlns:app="http://schemas.android.com/apk/res-auto"
app:input_icon 调用属性设置需要与前面定义(attrs.xml)的属性名字一致
<?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-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <include layout="@layout/activity_nav_bar"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/logo" android:layout_gravity="center" android:layout_marginTop="@dimen/loginmarginSize"/> <com.example.musicdemo.views.InputLayout android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/phone" app:input_hint="手机号" app:is_password="false"/> <TextView style="@style/viewline"/> <com.example.musicdemo.views.InputLayout android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/pssword" app:input_hint="密码" app:is_password="true"/> <TextView style="@style/viewline"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/marginSize" android:layout_gravity="center_horizontal" android:text="立即注册" android:onClick="onRegisterClick"/> <Button style="@style/conmmitBtn" android:text="登 录"/></LinearLayout>
7.6.1注册按钮按下有高亮效果,所以需要,创建资源文件2个,选择文件1个
commit_btn_n.xml 默认效果
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"> <!--实体颜色--> <solid android:color="@color/mainColor"/> <!--圆角--> <corners android:radius="@dimen/radius"/></shape>
commit_btn_h 高亮效果
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"> <!--实体颜色--> <solid android:color="@color/colorAccent"/> <!--圆角--> <corners android:radius="@dimen/radius"/></shape>
btn_commit_selector 选择效果、
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <!--高亮--> <item android:state_focused="true" android:drawable="@drawable/commit_btn_h"/> <!--按下--> <item android:state_pressed="true" android:drawable="@drawable/commit_btn_h"/> <!--选中--> <item android:state_selected="true" android:drawable="@drawable/commit_btn_h"/> <!--默认状态--> <item android:drawable="@drawable/commit_btn_n"/></selector>
到目前为止完成的效果如下!
8.登录的校验管理(引用一个牛逼的工具类),校验手机号码是否合法
官网:https://blankj.com/2016/07/31/android-utils-code/
8.1首先引入类
implementation 'com.blankj:utilcode:1.25.8
8.2在Appliction初始化工具类
//工具类初始化 Utils.init(this);
8.3 创建一个校验手机号码和密码是否为空的工具类(存在utils包下)
/** * 包名:com.example.musicdemo.utils * 用户:Administrator * 时间:15:16 2019-11-05 * 描述:登录验证工具类 */public class UserUtils { public static boolean validateLogin(Context context,String phone,String password){ //精确匹配 if(! RegexUtils.isMobileExact(phone)){ Toast.makeText(context,"请输入正确的手机号码",Toast.LENGTH_LONG).show(); return false; } if(TextUtils.isEmpty(password)){ Toast.makeText(context,"请输入密码",Toast.LENGTH_LONG).show(); return false; } return true; } }
8.4在登陆的Acitity完成跳转逻辑,还得把布局文件的输入框加入id
package com.example.musicdemo.activitys; import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.EditText; import com.example.musicdemo.R;import com.example.musicdemo.utils.UserUtils;import com.example.musicdemo.views.InputLayout; /** * 包名:com.example.musicdemo.activitys * 用户:Administrator * 时间:16:22 2019-10-23 * 描述: */public class LoginAcitivity extends BaseActivity{ private InputLayout ed_phone; private InputLayout ed_password; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); initView(); } //初始化View public void initView(){ initNavBar(false,"登录",false); ed_phone = fd(R.id.ed_phone); ed_password = fd(R.id.ed_passowrd); } //注册点击事件 public void onRegisterClick(View v){ } //登录按钮 public void onLoginClick(View v){ String phone = ed_phone.getInputStr(); String password = ed_password.getInputStr(); if(!UserUtils.validateLogin(this,phone,password)){ return; } Intent intent = new Intent(this,MainActivity.class); startActivity(intent); finish(); }}
9.完成注册页面,页面和登录页面大同小异的
9.1新建Activity(RegisteredAcitivity)简单的加载Layout布局和初始化下状态栏
public class RegisteredAcitivity extends BaseActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_registered); init(); } public void init(){ initNavBar(true,"注册",false); }}
9.2.创建注册页面的Layout(activity_registered)
<?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-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <include layout="@layout/activity_nav_bar"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/logo" android:layout_gravity="center" android:layout_marginTop="@dimen/loginmarginSize"/> <com.example.musicdemo.views.InputLayout android:id="@+id/input_phone" android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/phone" app:input_hint="手机号" app:is_password="false"/> <TextView style="@style/viewline"/> <com.example.musicdemo.views.InputLayout android:id="@+id/input_passowrd" android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/pssword" app:input_hint="密码" app:is_password="true"/> app:is_password="true"/> <TextView style="@style/viewline"/> <com.example.musicdemo.views.InputLayout android:id="@+id/confirm_passowrd" android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/pssword" app:input_hint="确认密码" app:is_password="true"/> <TextView style="@style/viewline"/> <Button style="@style/conmmitBtn" android:text="注 册" android:onClick="onLoginClick"/></LinearLayout>
9.3去登录页面添加点击“立即注册”的跳转逻辑
//注册点击事件 public void onRegisterClick(View v){ Intent intent = new Intent(this,RegisteredAcitivity.class); startActivity(intent); }
10.添加动画,效果如下,新的Activity从右往左进入,原Activity缩小,返回的时候,新的Activity从左往右移动,原Activity从小放大
10.1 修改style文件,添加引用动画的item,引用AnimtionActivity
<item name="android:windowAnimationStyle">@style/AnimtionActivity</item>
10.2创建AnimtionActivity动画<style>提供全局使用
<style name="AnimtionActivity" parent="@android:style/Animation.Activity"> <!--打开Activity新Activity执行动画--> <item name="android:activityOpenEnterAnimation">@anim/open_enter</item> <!--原Activity执行动画--> <item name="android:activityOpenExitAnimation">@anim/open_exit</item> <!--退出Activity执行动画--> <item name="android:activityCloseExitAnimation">@anim/close_exit</item> <!--退出Acitivyt后重新显示执行的动画--> <item name="android:activityCloseEnterAnimation">@anim/close_enter</item> </style>
10.3创建动画文件(创建一个anim存放动画文件)
10.4 4个文件内容如下
open_enter.xml
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="100%" android:toXDelta="0%" android:duration="@integer/anim_duration"/> <!--从左到右显示Acitity--> <!--新Activity执行动画--></set>
open_exit.xml
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:fromXScale="1.0" android:fromYScale="1.0" android:toXScale="0.5" android:toYScale="0.5" android:pivotX="50%" android:pivotY="50%" android:duration="@integer/anim_duration"/> <!--退出的缩放动画--> <!--原Activity执行动画--></set>
close_exit.xml
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="0%" android:toXDelta="100%" android:duration="@integer/anim_duration"/> <!--退出Activity执行动画--></set>
10.5动画时长引用的文件在是values单独创建的一个文件
<?xml version="1.0" encoding="utf-8"?><resources> <integer name="anim_duration">1000</integer> </resources>
11.添加个人中心页面,以及完成跳转
11.2创建对应的Activity(MeActivity)和布局文件(activity_me.xml)
/** * 包名:com.example.musicdemo.activitys * 用户:Administrator * 时间:10:48 2019-11-07 * 描述: */public class MeActivity extends BaseActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_me); } //修改密码 public void onChangeClick(View view){ Intent intent = new Intent(this,ChangePasswordActivity.class); startActivity(intent); } //退出登录 public void onOutLogin(View view){ UserUtils.logout(this); }}
11.3修改密码部分调用的跟之前一样的按下高亮效果 (此处省略)
<?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/activity_nav_bar"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:text="用户名" android:textStyle="bold" android:layout_marginTop="@dimen/marginSize"/> <TextView android:layout_width="match_parent" android:layout_height="@dimen/itemHeight" android:gravity="center_vertical" android:text="修改密码" android:paddingLeft="@dimen/marginSize" android:onClick="onChangeClick" android:background="@drawable/item_commit_selector"/> <Button style="@style/conmmitBtn" android:text="退出登录" android:onClick="onOutLogin"/></LinearLayout>
11.2进入BaseAcitivity添加个人中心跳转
//跳转个人中心 mIv_me.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(BaseActivity.this,MeActivity.class); startActivity(intent); } });
12.创建修改密码界面
public class ChangePasswordActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_changeassword); }}
<?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-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <include layout="@layout/activity_nav_bar"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/logo" android:layout_gravity="center" android:layout_marginTop="@dimen/loginmarginSize"/> <com.example.musicdemo.views.InputLayout android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/pssword" app:input_hint="原密码" app:is_password="false"/> <TextView style="@style/viewline"/> <com.example.musicdemo.views.InputLayout android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/pssword" app:input_hint="新密码" app:is_password="true"/> app:is_password="true"/> <TextView style="@style/viewline"/> <com.example.musicdemo.views.InputLayout android:layout_width="match_parent" android:layout_height="@dimen/inputView_height" app:input_icon="@mipmap/pssword" app:input_hint="确认密码" app:is_password="true"/> <TextView style="@style/viewline"/> <Button style="@style/conmmitBtn" android:text="确 认" android:onClick="onLoginClick"/></LinearLayout>
13.完成退出登录跳转并解决因为Task堆栈导致的错误
13.1在工具类增加跳转方法
Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK 反正退出登录后可以返回
((Activity)context).overridePendingTransition(R.anim.open_enter,R.anim.open_exit);解决因为重新创建堆栈导致的动画异常,重新指定跳转动画
/** * 退出登录 * */ public static void logout(Context context){ Intent intent = new Intent(context, LoginAcitivity.class); //解决跳转到登录界面后可以返回前面的界面 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); //解决因为重新创建堆栈导致的动画异常,重新指定跳转动画 ((Activity)context).overridePendingTransition(R.anim.open_enter,R.anim.open_exit); }
13.2在MeActivity引用
//退出登录 public void onOutLogin(View view){ UserUtils.logout(this); }