自定义控件(19)---自定义控件之高仿猫眼小项目(1)

来源:互联网 发布:淘宝交钱需要交多少钱 编辑:程序博客网 时间:2024/06/07 03:49

源码下载 高仿猫眼电影App


工作中跟同事一起利用工作中空挡时间,各写了一个高仿猫眼电影app的小项目

我写的太渣渣了,所以我把同事大神的代码贴出来了,自己写了备注

哎。。。由于后来的那个下拉刷新效果真心绕,那个效果就不贴代码了,如果大家有兴趣可以去下载,由于时间原因,只搭建了个逻辑框架,另外2个不错的效果,绝对我搭档原创,我搭档神速完成,膜拜中。。。

首先来看入口界面的布局文件吧

activity_dandy_layout.xml

<RelativeLayout 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"    android:background="@android:color/white" >    <FrameLayout        android:id="@+id/content"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_above="@+id/devider"        android:background="@android:color/transparent" />    <View        android:id="@+id/devider"        android:layout_width="match_parent"        android:layout_height="1dp"        android:layout_above="@+id/tabLayout"        android:background="@color/background_bg2" /><!--  需要设置宽高属性,这里先标红色,以后在贴布局 -->   <span style="color:#ff0000;"> <include        android:id="@+id/tabLayout"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        layout="@layout/tab_layout" /></span></RelativeLayout>

为了对fragment进行更好的管理,这里用到了ActivityStack管理

ActivityUtil

package com.dandy.utils;import java.util.Stack;import android.app.Activity;import android.content.Context;/** * Activity管理工具类 每次启动新的Activity,都会将此压入Activity Stack,当用户执行返回操作时,移除Activity * Stack顶上的Activity */public class ActivityUtil {/** * 私有Util工具类构造函数 * 生成私有的activityStack实例 */private Stack<Activity> activityStack;private ActivityUtil() {if (activityStack == null) {activityStack = new Stack<Activity>();}}/** * 单一实例 */private static ActivityUtil activityUtil;public static ActivityUtil getInstance() {if (activityUtil == null) {synchronized (ActivityUtil.class) {if (activityUtil == null) {activityUtil = new ActivityUtil();}}}return activityUtil;}/** * 添加Activity到堆栈 */public void addActivity(Activity activity) {activityStack.add(activity);}/** * 获取当前Activity(堆栈中最后一个压入的) */public Activity getCurrentActivity() {Activity activity = activityStack.lastElement();return activity;}/** * 结束当前Activity(堆栈中最后一个压入的)???????????? */public void finishCurrentActivity() {finishThisActivity(getCurrentActivity());}/** * 結束Activity??????????? */public void finishThisActivity(Activity activity) {if (activity != null) {activityStack.remove(activity);activity.finish();activity = null;}}/** * 结束指定类名的Activity */public void finishActivity(Class<?> cls) {for (Activity activity : activityStack) {if (activity.getClass().equals(cls)) {finishThisActivity(activity);}}}/** * 结束所有Activity */public void finishAllActivity() {for (int i = 0, size = activityStack.size(); i < size; i++) {if (null != activityStack.get(i)) {Activity activity = activityStack.get(i);activityStack.remove(activity);activity.finish();}}activityStack.clear();activityStack = null;}/** * 退出应用程序 */public void AppExit(Context context) {try {finishAllActivity();System.exit(0);} catch (Exception e) {}}}

因为主界面是继承FragmentActivity,我们也来个基类的

BaseFragmentActivity

package com.dandy.weights.baseWeights;import com.dandy.utils.ActivityUtil;import android.os.Bundle;import android.support.v4.app.FragmentActivity;/** * 基类抽取 */public abstract class BaseFragmentActivity extends FragmentActivity{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//一旦启动,就添加到Stack中ActivityUtil.getInstance().addActivity(this);//让子类去实现自己的初始化数据bindData();//让子类去加载各自的布局文件setContentView();//让子类去初始化控件initView();//让子类自己去实现自己的点击事件setOnClickListener();}/** * 捕捉android中的后退键事件--栈的操作 */@Overridepublic void onBackPressed() {//从Stack中结束本界面ActivityUtil.getInstance().finishThisActivity(this);super.onBackPressed();}/** *初始化数据源  */public abstract void bindData();/** *设置 ContentView布局 */public abstract void setContentView();/** *初始化控件  */public abstract void initView();/** *设置监听事件  */public abstract void setOnClickListener();}

一步一步来接下来看DandyActivity(注释很清楚了)
package com.demo.dandy;import android.annotation.SuppressLint;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentTransaction;import android.view.MotionEvent;import android.view.View;import com.dandy.weights.TabItemView;import com.dandy.weights.TabItemView.TabClickListner;import com.dandy.weights.baseWeights.BaseFragmentActivity;import com.demo.dandy.fragments.CinemaFragment;import com.demo.dandy.fragments.CommunityFragment;import com.demo.dandy.fragments.MineFragment;import com.demo.dandy.fragments.MovieFragment;/** * 主项目入口。。 * @author Administrator */public class DandyActivity extends BaseFragmentActivity implementsTabClickListner {/***顶部Tab栏目*/private TabItemView movie, cinema, community, mine, lastSelectedTab;/**整体界面包含电影。影院。发现。我的4个切换界面*/private MovieFragment movieFragment;private CinemaFragment cinemaFragment;private MineFragment mineFragment;private CommunityFragment communityFragment;/**fragment管理器*/private FragmentManager fragmentManager;@Overridepublic void setContentView() {//整体的app分为2部分--出去底部的Tab栏目是一部分,剩余的是一部分(FrameLayout填充即可)setContentView(R.layout.activity_dandy_layout);//获取fragment管理器fragmentManager = getSupportFragmentManager();}@Overridepublic void initView() {//底部Tab栏视图获取View tabLayout = findViewById(R.id.tabLayout);/** * 因为底部Tag栏目是include进来的 * 需要在tabLayout布局里面进行获取Tab栏目对应的按钮选项 */movie = (TabItemView) tabLayout.findViewById(R.id.movie);cinema = (TabItemView) tabLayout.findViewById(R.id.cinema);community = (TabItemView) tabLayout.findViewById(R.id.community);mine = (TabItemView) tabLayout.findViewById(R.id.mine);//初始化电影Tab处于选中状态movie.setTabSelected(true);lastSelectedTab = movie;//默认显示电影fragment界面setTabSelection(0);}/** * 设置Tab栏目各个选项的监听 */<span style="color:#ff0000;">@Overridepublic void setOnClickListener() {movie.setTabClickListener(this);cinema.setTabClickListener(this);community.setTabClickListener(this);mine.setTabClickListener(this);}</span>/** * 当点击每个Tab栏目的按钮,进行的函数回调操作 */@Overridepublic void onTabClick(View view) {switch (view.getId()) {case R.id.movie:// index = 0;//首先判断是不是点击的同一个Tab栏目按钮judgeTabSame(movie);//进行点击时候的一些操作setTabSelection(0);break;case R.id.cinema:// index = 1;judgeTabSame(cinema);setTabSelection(1);break;case R.id.community:// index = 2;judgeTabSame(community);setTabSelection(2);break;case R.id.mine:// index = 3;judgeTabSame(mine);setTabSelection(3);break;}}/** * 判断是否是同一个tab标签,并设置状态 * 如果点击的不是同一个按钮选项,就将上次的选中按钮设置false * 将最新点击的按钮设置为true * 对于设置false,true进行了什么操作,去看自定义控件里面的操作即可 *  */private void judgeTabSame(TabItemView clickTab) {if (lastSelectedTab != clickTab) {lastSelectedTab.setTabSelected(false);lastSelectedTab = clickTab;lastSelectedTab.setTabSelected(true);}}/** * 根据传入的index参数来设置选中的tab页 */@SuppressLint("CommitTransaction")private void setTabSelection(int index) {// 开启一个Fragment事务FragmentTransaction transaction = fragmentManager.beginTransaction();//显示某个fragment之前,将所有的fragment隐藏,在显示需要显示的fragment即可hideAllFragment(transaction);switch (index) {case 0://如果为空,就创建,并且到对应的frameLayout进行填充if (movieFragment == null) {movieFragment = new MovieFragment();transaction.add(R.id.content, movieFragment);} else {//如果存在,就直接显示即可transaction.show(movieFragment);}break;case 1:if (cinemaFragment == null) {cinemaFragment = new CinemaFragment();transaction.add(R.id.content, cinemaFragment);} else {transaction.show(cinemaFragment);}break;case 2:if (communityFragment == null) {communityFragment = new CommunityFragment();transaction.add(R.id.content, communityFragment);} else {transaction.show(communityFragment);}break;case 3:if (mineFragment == null) {mineFragment = new MineFragment();transaction.add(R.id.content, mineFragment);} else {transaction.show(mineFragment);}break;}transaction.commit();}/** * 隐藏掉所有的fragment,防止多个fragment显示 */private void hideAllFragment(FragmentTransaction transaction) {if (movieFragment != null) {transaction.hide(movieFragment);}if (cinemaFragment != null) {transaction.hide(cinemaFragment);}if (communityFragment != null) {transaction.hide(communityFragment);}if (mineFragment != null) {transaction.hide(mineFragment);}}@Overridepublic void bindData() {// TODO Auto-generated method stub}}


到这里由于主界面的代码里面设置到了底部Tab栏目的点击回调事件,接下来就可以将自定义的布局贴出来了,这里可以看下那个主页面布局的include标签--刚开始的布局标红色的部分,接下来就是它的布局如下

tab_layout.xml--里面是自定义控件属性

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tabItem="http://schemas.android.com/apk/res/com.demo.dandy"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="horizontal"     android:background="@color/background_bg7"    android:paddingTop="@dimen/tab_padding"    android:paddingBottom="@dimen/tab_padding">    <com.dandy.weights.TabItemView        android:id="@+id/movie"        style="@style/TabItemStyle"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:gravity="center"        android:orientation="vertical"        tabItem:contentLogoBack="@drawable/selector_tab_movie"        tabItem:contentTextString="@string/movie" />    <com.dandy.weights.TabItemView        android:id="@+id/cinema"        style="@style/TabItemStyle"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:gravity="center"        android:orientation="vertical"        tabItem:contentLogoBack="@drawable/selector_tab_cinema"        tabItem:contentTextString="@string/cinema" />    <com.dandy.weights.TabItemView        android:id="@+id/community"        style="@style/TabItemStyle"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:gravity="center"        android:orientation="vertical"        tabItem:contentLogoBack="@drawable/selector_tab_community"        tabItem:contentTextString="@string/community" />    <com.dandy.weights.TabItemView        android:id="@+id/mine"        <span style="color:#ff0000;">style="@style/TabItemStyle"</span>        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:gravity="center"        android:orientation="vertical"        tabItem:contentLogoBack="@drawable/selector_tab_mine"        tabItem:contentTextString="@string/mine" /></LinearLayout>

上面的style属性
     <span style="color:#ff0000;"><!-- 使用自定义属性时,只需要把包名写上即可,如下方式: --></span>    <style name="TabItemStyle">        <item name="com.demo.dandy:contentTextSize">@dimen/tab_text_size</item>        <item name="com.demo.dandy:contentLogoSize">@dimen/tab_logo_size</item>        <item name="com.demo.dandy:contentTextColor">@color/textcolor_black_b3</item>    </style>    
另外attrs.xml里面
    <declare-styleable name="TabItemView">        <attr name="contentTextSize" format="dimension"/> <!-- 字体大小 -->        <attr name="contentTextColor" format="color"/> <!-- 字体颜色 -->        <attr name="contentTextString" format="string"/> <!-- 显示的默认文字 -->        <attr name="contentLogoBack" format="reference"/> <!-- item背景 -->        <attr name="contentLogoSize" format="dimension" />    </declare-styleable>

接下里就可以看自定义底部Tab栏的代码了(注释很清楚)

TabItemView

package com.dandy.weights;import com.dandy.utils.PhoneUtils;import com.demo.dandy.R;import android.content.Context;import android.content.res.TypedArray;import android.text.TextUtils;import android.util.AttributeSet;import android.util.TypedValue;import android.view.InflateException;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import android.view.View.OnClickListener;public class TabItemView extends LinearLayout implements OnClickListener{private Context mContext;private ImageView contentLogo;private TextView contentText;private TabClickListner mClickListner;/** * 构造函数 */public TabItemView(Context context) {this(context, null);}public TabItemView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public TabItemView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);this.mContext = context;init(attrs);addView();}/** * 初始化attrs--自定义的属性 *  <declare-styleable name="TabItemView">        <attr name="contentTextSize" format="dimension"/> <!-- 字体大小 -->        <attr name="contentLogoSize" format="dimension" /><!-- 图片大小-->        <attr name="contentTextColor" format="color"/> <!-- 字体颜色 -->                <attr name="contentTextString" format="string"/> <!-- 显示的默认文字 -->        <attr name="contentLogoBack" format="reference"/> <!-- item背景 -->        </declare-styleable>                //通过style的方式也可以进行自定义控件,只需要前面加上包名即可       <style name="TabItemStyle">        <item name="com.demo.dandy:contentTextSize">@dimen/tab_text_size</item>        <item name="com.demo.dandy:contentLogoSize">@dimen/tab_logo_size</item>        <item name="com.demo.dandy:contentTextColor">@color/textcolor_black_b3</item>       </style>     *///背景图片,字体(呵呵,不公有吧)private int logoBackResourceId;private String textString;//style里面的属性--共有的--图片大小、字体颜色、字体大小(呵呵都是共有的)private int contentLogoSize;private int textColor;private float textSize;//设置下字体的默认大小吧(如果在获取属性失败的前提下哦)private static final float defaultTextSize = 16;//默认的字体颜色,选中的字体颜色(进行切换时候转换使用)private int defaultColor,selectedColor;/** * 初始化自定义属性 * @param attrs */private void init(AttributeSet attrs){/**设置点击监听事件*/this.setOnClickListener(this);//从attrs.xml中获取自定义属性     (AttributeSet set, int[] attrs)TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.TabItemView);//图片资源logoBackResourceId = ta.getResourceId(R.styleable.TabItemView_contentLogoBack, -1);//显示字体textString = ta.getString(R.styleable.TabItemView_contentTextString);//图片大小contentLogoSize = ta.getDimensionPixelSize(R.styleable.TabItemView_contentLogoSize, LayoutParams.WRAP_CONTENT);//字体颜色textColor = ta.getColor(R.styleable.TabItemView_contentTextColor, getResources().getColor(android.R.color.black));//字体大小textSize = ta.getDimensionPixelSize(R.styleable.TabItemView_contentTextSize, PhoneUtils.dp2px(mContext, defaultTextSize));ta.recycle();defaultColor = mContext.getResources().getColor(R.color.textcolor_black_b3);selectedColor = mContext.getResources().getColor(R.color.textcolor_red_d);}/** * 添加各个控件 */private void addView(){/** * 需要自定义属性里面的logoBackResourceId、contentLogoSize */contentLogo = new ImageView(mContext);contentLogo.setFocusable(false);contentLogo.setClickable(false);//设置图片的参数--制定宽高LayoutParams logoParams = new LayoutParams(contentLogoSize,contentLogoSize);contentLogo.setLayoutParams(logoParams);//创建布局完毕,需要设置背景图片if(logoBackResourceId != -1){contentLogo.setBackgroundResource(logoBackResourceId);}else{throw new InflateException("未设置填充图片资源");}this.addView(contentLogo);/** * 需要自定义属性里面的textString、textColor、textSize */if(!TextUtils.isEmpty(textString)){contentText = new TextView(mContext);contentText.setFocusable(false);contentText.setClickable(false);//设置字体的参数LayoutParams textParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);textParams.topMargin = PhoneUtils.dp2px(mContext,3);contentText.setLayoutParams(textParams);contentText.setTextColor(textColor);contentText.setTextSize(TypedValue.COMPLEX_UNIT_PX,textSize);contentText.setText(textString);this.addView(contentText);}}@Overridepublic void onClick(View v) {setTabSelected(true);if(mClickListner != null){//让外部去实现点击事件的回调处理mClickListner.onTabClick(this);}}/** *设置共有的点击事件处理 */public void setTabSelected(boolean enable){if(contentLogo != null){contentLogo.setSelected(enable);}if(contentText != null){if(enable){contentText.setTextColor(selectedColor);}else{contentText.setTextColor(defaultColor);}}}public interface TabClickListner{void onTabClick(View view);}/** *设置点击监听事件  */public void setTabClickListener(TabClickListner listner){this.mClickListner = listner;}}

另外上面还需要工具类
package com.dandy.utils;import android.content.Context;public class PhoneUtils {public static int dp2px(Context context,float dpValue){float scale = context.getResources().getDisplayMetrics().density;return (int)(dpValue * scale +0.5f);}public static int Px2Dp(Context context, float px) {     final float scale = context.getResources().getDisplayMetrics().density;     return (int) (px / scale + 0.5f); } }

到此为止就可以实现,点击底部Tab栏目实现底部切换了,接下来就是实现上半部分的frameLayout布局了,也就是对应的4个页面即可



0 0
原创粉丝点击