android 仿Boss直聘訊息介面
最近好久沒有更新我的部落格了,之前5月份由於在一家苦逼的初創公司,一直有時間更新blog,上週離職了,好好休息了兩天,上週面了一家公司感覺挺好的,這兩天一邊找工作一邊寫寫部落格吧。祝我順利等到offer吧^_^(不過北京這一段時間的Android行情不是特別好啊)
好了,開始今天的主題,在找工作用的是Boss直聘,感覺APP裡面的訊息介面還不錯,就試著自己寫了一個。以後有時間把其他介面也寫一下,先看一下GIF效果。
接下來簡單說一下吧!
先來看一下main佈局檔案吧,比較簡單,先看一下圖片
直接看程式碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.cxy.boss.ui.MainActivity">
<RadioGroup
android:id="@+id/main_rg"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:weightSum="4" >
<RadioButton
android:id="@+id/rb_find_nor"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:button="@null"
android:checked="true"
android:drawableTop="@drawable/rb_find_nor_selecter"
android:text="職位"
android:textSize="12sp"
android:gravity="center"/>
<RadioButton
android:id="@+id/rb_company"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:button="@null"
android:drawableTop="@drawable/rb_company_selecter"
android:text="公司"
android:textSize="12sp"
android:gravity="center"/>
<RadioButton
android:id="@+id/rb_message"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:button="@null"
android:drawableTop="@drawable/rb_message_selecter"
android:text="訊息"
android:textSize="12sp"
android:gravity="center"/>
<RadioButton
android:id="@+id/rb_myPage"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:button="@null"
android:drawableTop="@drawable/rb_my_selecter"
android:text="我的"
android:textSize="12sp"
android:gravity="center"/>
</RadioGroup>
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#cacaca"
android:id="@+id/line"
android:layout_alignParentBottom="true"
android:layout_marginBottom="60dp"
/>
<FrameLayout
android:id="@+id/id_ll_top"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
</FrameLayout>
</RelativeLayout>
底部導航是一個RadioGroup ,裡面有四個RadioButton,根據radioButton 的狀態來動態切換四個Fragment達到切換頁面的效果。在佈局檔案裡的RadioButton的背景是
‘’android:drawableTop="@drawable/rb_message_selecter"
我們再來看一下rb_message_selecter.xml這個檔案。
drawable包下,有四個rb開頭的檔案,我們來看一下rb_message_selecter檔案,一共三行主要程式碼,第一行是item被選中的時候要顯示的圖片資源,第二行是未被選中時顯示的圖片資源,第三行顯示的是預設顯示的圖片資源。
接下來看一下MainActivity檔案
MainActivity.java
package com.cxy.boss.ui;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.KeyEvent;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.cxy.boss.R;
import com.cxy.boss.fragment.Company_Fragment;
import com.cxy.boss.fragment.Find_Fragment;
import com.cxy.boss.fragment.Message_Fragment;
import com.cxy.boss.fragment.My_Fragment;
public class MainActivity extends FragmentActivity implements RadioGroup.OnCheckedChangeListener {
static long exittime = 0; //計算兩次Back鍵按下的時間
private RadioGroup group;
private FragmentManager manager; //Fragmnet的管理器
private FragmentTransaction transaction; //Fragment事物
private My_Fragment my_fragment; //我的頁面
private Find_Fragment find_fragment; //職位頁面
private Company_Fragment company_fragment; //公司頁面
private Message_Fragment message_fragment; //訊息頁面
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
/**
* @程式功能:佈局初始化
* @autho:cxy
* */
private void initView() {
// findById
group = (RadioGroup) findViewById(R.id.main_rg);
manager = getSupportFragmentManager();
transaction = manager.beginTransaction();
//對RadioGroup設定監聽
group.setOnCheckedChangeListener(this);
//職位的Fragmnet
find_fragment = new Find_Fragment();
//公司的Fragmnet
company_fragment = new Company_Fragment();
//訊息的Fragmnet
message_fragment = new Message_Fragment();
//我的Fragment
my_fragment = new My_Fragment();
//為事物添加布局頁面
transaction.add(R.id.id_ll_top,find_fragment);
transaction.add(R.id.id_ll_top,company_fragment);
transaction.add(R.id.id_ll_top,message_fragment);
transaction.add(R.id.id_ll_top,my_fragment);
// 隱藏收藏和我的介面
transaction.hide(company_fragment);
transaction.hide(my_fragment);
transaction.hide(message_fragment);
// 提交事物
transaction.commit();
//為group設定監聽
group.setOnCheckedChangeListener(this);
}
@Override
public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
//根據RaidoButton切換對應的頁面
transaction = manager.beginTransaction();
switch (checkedId){
case R.id.rb_find_nor:
transaction.show(find_fragment);
transaction.hide(company_fragment);
transaction.hide(message_fragment);
transaction.hide(my_fragment);
break;
case R.id.rb_company:
transaction.show(company_fragment);
transaction.hide(find_fragment);
transaction.hide(message_fragment);
transaction.hide(my_fragment);
break;
case R.id.rb_message:
transaction.show(message_fragment);
transaction.hide(company_fragment);
transaction.hide(find_fragment);
transaction.hide(my_fragment);
break;
case R.id.rb_myPage:
transaction.show(my_fragment);
transaction.hide(company_fragment);
transaction.hide(message_fragment);
transaction.hide(find_fragment);
break;
default:
break;
}
//提交事務
transaction.commit();
}
//監聽返回鍵
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK){
this.exitapp();
}
return true;
}
/**
* 程式從功能:
* 退出程式
* **/
private void exitapp() {
if ((System.currentTimeMillis() - exittime)>2000){
Toast.makeText(this,"再按一次返回鍵退出程式",Toast.LENGTH_SHORT).show();
exittime = System.currentTimeMillis();
}else{
System.exit(0);
}
}
}
程式碼裡註釋比較詳細,在這裡就不多解釋了,這樣就大概構成了boss直聘的最基本框架,下面來看一下Message_Fragment.java這個檔案,
package com.cxy.boss.fragment;
import android.graphics.Color;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.cxy.boss.R;
import java.util.ArrayList;
import java.util.List;
/**
* 訊息
* Created by cxy on 2016/11/14.
*/
public class Message_Fragment extends Fragment implements View.OnClickListener{
private ViewPager viewPager;
//頂部的聊天、互動的button
private Button btn_chat,btn_exchange;
// 聊天的Fragment
private Msg_chat_Fragment msg_chat_fragment;
//互動的Fragment
private Msg_exchange_Fragment msg_exchange_fragment;
// fragment的容器
private List<Fragment> fragments;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_message,null);
initView(view);
initPager();
return view;
}
/**
* 佈局初始化
* */
private void initView(View view) {
btn_chat = (Button)view. findViewById(R.id.btn_chat);
btn_exchange = (Button)view. findViewById(R.id.btn_exchange);
viewPager = (ViewPager)view. findViewById(R.id.msg_vp);
btn_chat.setOnClickListener(this);
btn_exchange.setOnClickListener(this);
}
//監聽聊天和互動的點選事件
@Override
public void onClick(View view) {
switch (view.getId()){
//如果是聊天的Button點選
case R.id.btn_chat:
//設定viewPager選中第1個頁面
viewPager.setCurrentItem(0);
btn_chat.setTextColor(Color.rgb(83,202,195));
btn_chat.setBackgroundColor(Color.rgb(255,255,255));
btn_exchange.setTextColor(Color.rgb(255,255,255));
btn_exchange.setBackgroundColor(Color.rgb(83,202,195));
break;
// 如果是互動的Button點選
case R.id.btn_exchange:
//設定viewPager選中第2個頁面
viewPager.setCurrentItem(1);
// 設定文字顏色和button背景色
btn_exchange.setTextColor(Color.rgb(83,202,195));
btn_exchange.setBackgroundColor(Color.rgb(255,255,255));
btn_chat.setTextColor(Color.rgb(255,255,255));
btn_chat.setBackgroundColor(Color.rgb(83,202,195));
break;
}
}
/**
* 初始化ViewPager
* */
private void initPager(){
fragments = new ArrayList<Fragment>();
msg_chat_fragment = new Msg_chat_Fragment();
msg_exchange_fragment = new Msg_exchange_Fragment();
fragments.add(msg_chat_fragment);
fragments.add(msg_exchange_fragment);
//設定介面卡
viewPager.setAdapter(new MyPagerAdapter(getFragmentManager()));
//設定viewPager的監聽
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
//根據position的值來改變頂部button的切換。文字顏色和背景顏色
if (position==0){
btn_chat.setTextColor(Color.rgb(83,202,195));
btn_chat.setBackgroundColor(Color.rgb(255,255,255));
btn_exchange.setTextColor(Color.rgb(255,255,255));
btn_exchange.setBackgroundColor(Color.rgb(83,202,195));
}else{
btn_exchange.setTextColor(Color.rgb(83,202,195));
btn_exchange.setBackgroundColor(Color.rgb(255,255,255));
btn_chat.setTextColor(Color.rgb(255,255,255));
btn_chat.setBackgroundColor(Color.rgb(83,202,195));
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
// viewPager介面卡
class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
}
註釋比較詳細,就不做過多解釋了。
再來看一下訊息的Fragment檔案
package com.cxy.boss.fragment;
import android.graphics.Color;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.cxy.boss.R;
import java.util.ArrayList;
import java.util.List;
/**
* 互動頁面
* Created by cxy on 2016/11/14.
*/
public class Msg_exchange_Fragment extends Fragment implements View.OnClickListener{
//申明ViewPager
private ViewPager vp;
//申明相關元件
private TextView tv_likeMe;
private TextView tv_seeMe;
private TextView tv_newJob;
private List<Fragment> fragments;
//對我有意,誰看過我,新職位的Fragment
private Exc_Likeme_Fragment likeMeFragment;
private Exc_Seeme_Fragment seeMeFragment;
private Exc_newJob_Fragment newJobFragment;
// 定義必要變數和元件
// 看到width這個變數, 是用來控制綠色線條的寬度,與Display、LayoutParams 配合使用。
private int width;
private Display mDisplay;
private LinearLayout.LayoutParams params;
private View mViewLine;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_msg_exchange,null);
initView(view);
return view;
}
/**
* 佈局初始化
* */
private void initView(View view) {
fragments = new ArrayList<>();
likeMeFragment = new Exc_Likeme_Fragment();
seeMeFragment = new Exc_Seeme_Fragment();
newJobFragment = new Exc_newJob_Fragment();
//像list裡新增fragment
fragments.add(likeMeFragment);
fragments.add(seeMeFragment);
fragments.add(newJobFragment);
// findID
vp = (ViewPager) view.findViewById(R.id.exce_vp);
mViewLine = (View)view.findViewById(R.id.tab_line);
tv_likeMe = (TextView) view.findViewById(R.id.tv_likeMe);
tv_seeMe = (TextView) view.findViewById(R.id.tv_seeMe);
tv_newJob = (TextView) view.findViewById(R.id.tv_newJob);
//設定監聽
tv_likeMe.setOnClickListener(this);
tv_seeMe.setOnClickListener(this);
tv_newJob.setOnClickListener(this);
//設定介面卡
vp.setAdapter(new MyPagerAdapter(getFragmentManager()));
// 獲取當前視窗的display
mDisplay = getActivity().getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics();
mDisplay.getMetrics(outMetrics);
width = outMetrics.widthPixels/3;
params = (LinearLayout.LayoutParams)mViewLine.getLayoutParams();
params.width = width;
mViewLine.setLayoutParams(params);
vp.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(positionOffset != 0 && positionOffsetPixels != 0 ){
params.leftMargin = positionOffsetPixels / fragments.size() + width * position ;
mViewLine.setLayoutParams(params);
}
}
// 根據選擇的狀態給便能字型顏色
@Override
public void onPageSelected(int position) {
switch (position){
case 0:
tv_likeMe.setTextColor(Color.rgb(153,153,153));
tv_seeMe.setTextColor(Color.rgb(187,187,187));
tv_newJob.setTextColor(Color.rgb(187,187,187));
break;
case 1:
tv_seeMe.setTextColor(Color.rgb(153,153,153));
tv_likeMe.setTextColor(Color.rgb(187,187,187));
tv_newJob.setTextColor(Color.rgb(187,187,187));
break;
case 2:
tv_newJob.setTextColor(Color.rgb(153,153,153));
tv_seeMe.setTextColor(Color.rgb(187,187,187));
tv_likeMe.setTextColor(Color.rgb(187,187,187));
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
/**
* 對頂部的Tab的TextView設定監聽來改變選中
* 的字型顏色和設定ViewPager到與點選項相匹配的position
*/
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.tv_likeMe:
vp.setCurrentItem(0);
tv_likeMe.setTextColor(Color.rgb(153,153,153));
tv_seeMe.setTextColor(Color.rgb(187,187,187));
tv_newJob.setTextColor(Color.rgb(187,187,187));
break;
case R.id.tv_seeMe:
vp.setCurrentItem(1);
tv_seeMe.setTextColor(Color.rgb(153,153,153));
tv_likeMe.setTextColor(Color.rgb(187,187,187));
tv_newJob.setTextColor(Color.rgb(187,187,187));
break;
case R.id.tv_newJob:
vp.setCurrentItem(2);
tv_newJob.setTextColor(Color.rgb(153,153,153));
tv_seeMe.setTextColor(Color.rgb(187,187,187));
tv_likeMe.setTextColor(Color.rgb(187,187,187));
break;
}
}
// 介面卡
class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
}
相關解釋都寫在程式碼裡了,現在來分析一下。
看到width這個變數, 是用來控制紅色線條的寬度,與Display、LayoutParams 配合使用。
pageCount主要是用來計算當前滑動的時候, 線條的左右邊距。
解題思路: 主要操作其實就是那條紅色的提示線。 當我們在滾動ViewPager的 時候, 只需要能夠動態的操作提示線的左邊距屬性MarginLeft即可。
所以我們需要對ViewPager提供滾動的監聽器, 其主要操作的方法就是public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
理解屬性:
position,當前選中的頁面位置(從0開始);
positionOffset, 大小從0到螢幕寬度或從螢幕寬度到0。 取決於你的左右滑動狀態。
positionOffsetPixels,
主要就是對我們手指狀態的一個操作。 值為1的時候, 正在滑動、 2: 手指擡起, 0: 結束了。
開始進入演算法邏輯:
在我們的onPageScrolled中的處理,
首先我們需要保證當前的ViewPager處於可滑動狀態。 否則就不要進行邏輯處理了,不然消費太大。
然後我們已經提前獲取到了提示線的LayoutParams物件(不要在該方法啊中獲取LayoutParams,
不然非常浪費記憶體) 。
params.leftMargin
= positionOffsetPixels / pageCount + width * position ; 可以看出我們一直在不停的給MarginLeft計算值
然後mViewLine.stLayoutParams(params);不停的進行繪製,
這樣可以有一種類似於動畫的效果體現。
計算:
positionOffsetPixels / pageCount: 我們有三個頁面, 這樣計算可以正常的分配每個Tab佔有的螢幕寬度值;width * position : width為提示線寬度, 這個值基本上就不需改變了。 position前面解釋過意思了。 這樣可以計算出我的當前MarginLeft的所需寬度大小。 (之後一直setLayoutParams就可以了。)
基本的邏輯都是比較清晰的, 也非常的容易懂。 (不喜請噴!!!)
最後放上demo的地址:
http://download.csdn.net/detail/xiaoyu940601/9868389