1. 程式人生 > >android 仿Boss直聘訊息介面

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