自定义控件03

12人阅读 评论(0) 收藏 举报
分类:

1.侧滑菜单
代码实现步骤
[1]先画主页面 代码如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:background="@drawable/top_bar_bg"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/btn_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/main_back"
            />
        <ImageView
            android:layout_width="wrap_content"
            android:background="@drawable/top_bar_divider"
            android:layout_height="wrap_content" />
        <TextView
            android:layout_width="match_parent"
            android:text="黑马新闻"
            android:textSize="30sp"
            android:textColor="#fff"
            android:gravity="center_horizontal"
            android:layout_height="wrap_content" />
    </LinearLayout>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="30sp"
        android:gravity="center"
        android:text="钓鱼岛是中国的\nxxx是世界的"
        />
</LinearLayout>

[2]画菜单页面

<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:layout_width="240dp"
    android:layout_height="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:background="@drawable/menu_bg"
        android:orientation="vertical" >
        <TextView
            style="@style/MenuText"
            android:background="#571F2C"
            android:drawableLeft="@drawable/tab_news"
            android:text="新闻" />
        <TextView
            style="@style/MenuText"
            android:drawableLeft="@drawable/tab_read"
            android:text="订阅" />
        <TextView
            style="@style/MenuText"
            android:drawableLeft="@drawable/tab_ties"
            android:text="跟帖" />
        <TextView
            style="@style/MenuText"
            android:drawableLeft="@drawable/tab_pics"
            android:text="图片" />
        <TextView
            style="@style/MenuText"
            android:drawableLeft="@drawable/tab_ugc"
            android:text="话题" />
        <TextView
            style="@style/MenuText"
            android:drawableLeft="@drawable/tab_vote"
            android:text="投票" />
        <TextView
            style="@style/MenuText"
            android:drawableLeft="@drawable/tab_focus"
            android:text="聚合阅读" />
    </LinearLayout>
</ScrollView>

3.把共同的属性抽到一个style里面

<style name="MenuText" parent="android:Widget.TextView">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:drawablePadding">15dp</item>
        <item name="android:gravity">center_vertical</item>
        <item name="android:padding">15dp</item>
        <item name="android:textColor">#fff</item>
        <item name="android:textSize">25sp</item>
        <item name="android:textStyle">bold</item>
    </style>

4.通过include 把2个孩子加入到main_activity页面

<?xml version="1.0" encoding="utf-8"?>
<com.itheima.custom02.SlidingMenu 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.itheima.custom02.MainActivity">
    <!--把刚刚做的2个布局 加到这个里面-->
    <include layout="@layout/sliding_menu"></include>
    <include layout="@layout/sliding_main"></include>
</com.itheima.custom02.SlidingMenu>

5.对孩子重新排版 重写onlayout方法

 //对布局重新排版
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        System.out.println("onlayout:"+l+"---:"+t+"---:"+r+"---"+b);
        //1.找到孩子
        View menuView = getChildAt(0);
        View mainView = getChildAt(1);
        //2.对孩子排版
        int menuWidth = menuView.getMeasuredWidth();
        menuView.layout(-menuWidth,t,l,b);
        mainView.layout(l,t,r,b);
    }

6.让容器消费事件,就是重写onTouchEvent方法 处理手指按下 和移动的逻辑

 switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN: //按下
                        //1.获取按下坐标
                        downX = event.getX();
                        break;
                    case MotionEvent.ACTION_MOVE: //移动
                        //2.获取移动后的坐标
                        float moveX = event.getX();
                        //3.算出移动距离
                        int distaceX = (int) (moveX - downX);
                        //4.对边界进行判断
                        if (distaceX <= 0){
                            distaceX = 0;
                        }else if (distaceX >=menuWidth){
                            distaceX = menuWidth;
                        }
                        scrollToX(distaceX);
                        break;
                    case MotionEvent.ACTION_UP:   //抬起
                        break;
            }

7.处理手指抬起的逻辑

case MotionEvent.ACTION_UP:   //抬起
                        if (distaceX <= menuWidth/2){
                            currentMainLeftPosition =0;
                            scrollToX(0);
                        }else{
                            scrollToX(menuWidth);
                            currentMainLeftPosition = menuWidth;
                        }
                        break;

8.让view 实现平滑滚动. Scroller
8.1先获取scroller实例

mScroller = new Scroller(getContext());

8.2 模拟X轴滚动的数据

int startX = distaceX;
                        int endX = currentMainLeftPosition;
                        int dx = endX - startX;
                        int duration = Math.abs(dx)*15;    //根据移动的距离来算时间
                        mScroller.startScroll(startX,0,dx,0,duration); //这个api 就会产生模拟滚动的数据
                        invalidate();  //---由于当前容器是viewGroup 会调用dispatchDraw--->drawChild---->child.draw--->computeScroll

8.3 取出模拟的数据

 @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            //1.获取我们模拟的数据
            int currX = mScroller.getCurrX();
            System.out.println("currX:"+currX);
            //2.让view滚动
            scrollToX(currX);
            //3.让computeScroll一直执行
            invalidate();
        }
    }

9.点击按钮实现菜单切换

 public void switchMenu() {
        int startX = 0;
        if (currentMainLeftPosition == 0){
            //说明菜单是关闭的 需要打开
            currentMainLeftPosition = menuWidth;
            startX = 0;
        }else{
            //说明菜单是打开的 需要关闭
            currentMainLeftPosition = 0;
            startX = menuWidth;
        }
        stratScrollerView(startX,currentMainLeftPosition);

    }

10.处理事件冲突. 如果x轴移动的距离 > Y轴移动的距离就拦截事件

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
                    case MotionEvent.ACTION_DOWN:  //按下
                        //获取按下坐标
                        downX =  ev.getX();
                        downY = ev.getY();
                        break;
                    case MotionEvent.ACTION_MOVE: //移动
                        //1.获取移动坐标
                        float moveX = ev.getX();
                        float moveY = ev.getY();
                        //2.算出X Y 轴移动的距离
                        int distanceX = (int) (moveX - downX);
                        int distaceY = (int)(moveY - downY);
                        if (Math.abs(distanceX )>Math.abs(distaceY)){
                            //说明用户按照X轴滑动 应该拦截事件
                            return  true;
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        break;
            }
        return super.onInterceptTouchEvent(ev); //默认返回false
    }

2.滚动条 viewPager

[1]在布局中声明控件

<android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="250dp">
    </android.support.v4.view.ViewPager>

[2]viewpager展示数据和listview一样需要一个适配器(pagerAdapter) 定义适配器

//定义viewpager适配器
    class MyPagerAdapter extends PagerAdapter{
        // 告诉ViewPager一共展示几条数据
        @Override
        public int getCount() {
            return vpLists.size();
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            System.out.println("isViewFromObject");
            return view == object;
        }
        //这个方法 相当于baseAdapter的getView 初始化viewpager的每个条目
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            System.out.println("instantiateItem");
            //1.把想展示的数据取出来
            ImageView iv = vpLists.get(position);
            //2.viewpager 需要自己手动把iv加入到容器(viewpager)里
            container.addView(iv);
            return iv;
        }
        //销毁不用的条目 参数3 对应的类型其实就是instantiateItem 返回的类型
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            System.out.println("destroyItem");
            //把用不到的条目移除
            container.removeView((View) object);
        }
    }

[3]添加小圆点和文本对应的布局

<?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.itheima.viewpager.MainActivity">
    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="250dp"></android.support.v4.view.ViewPager>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/vp"
        android:background="#66000000"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:paddingBottom="10dp"
        android:paddingTop="10dp">
        <TextView
            android:id="@+id/tv_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我是小圆点对应的文本"
            android:textColor="#fff"
            android:layout_marginBottom="5dp"
            android:textSize="23sp" />
       <!-- <View
            android:layout_width="6dp"
            android:background="@drawable/selector_dot"
            android:layout_height="6dp"/>-->
        <!--需要动态添加小圆点  根据加载的数据  有几条数据 就加载几个点-->
        <LinearLayout
            android:id="@+id/ll_dot"
            android:layout_width="wrap_content"
            android:orientation="horizontal"
            android:layout_height="wrap_content"></LinearLayout>
    </LinearLayout>
</RelativeLayout>

[4]初始化小圆点的逻辑

 //初始化小圆点的逻辑
    private void initDotData() {
        for (int i = 0; i < imageResIds.length; i++) {
            //1.动态创建5个View
            View dotView = new View(getApplicationContext());
            //2.给每个view设置默认的背景
            dotView.setBackgroundResource(R.drawable.selector_dot);
            //3.指定dotview大小 谷歌把布局中属性封装到了LayoutParams 里
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(7,7);
            //3.1 让每个小圆点有点距离
            if (i!=0){
                params.leftMargin = 7;
            }
            //4.把params设置给dotview
            dotView.setLayoutParams(params);
            //3.往线性布局里面加
            ll_dot.addView(dotView);
        }
    }

[5]当滑动viewpager的时候 ,改变小圆点的状态并且还要改变对应文本信息,就是给viewpager设置滑动的监听

vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            //当页面滑动的时候调用
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
            //当一个新的页面被选中的时候调用
            @Override
            public void onPageSelected(int position) {
                changeDotAndText(position);
            }
            //当滚动的状态发生改变
            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

[6]由于改变小圆点的状态和对应文本信息这个逻辑用到了2次 所以抽出一个方法调用

//作用就是用来改变小圆点的状态 和 对应文本的信息
    private void changeDotAndText(int position) {
        //1.改变小圆点的状态和对应文本的内容
        tv_desc.setText(descs[position]);
        //2.找到小圆点
        ll_dot.getChildAt(position).setSelected(true);
        //3.如果currentSelectDot不为null 就说明这个点是选中状态
        if (currentSelectDot!=null){
            currentSelectDot.setSelected(false);
        }
        currentSelectDot = ll_dot.getChildAt(position);
    }

[7]实现无限循环 原理:就是在适配器getcount方法里面返回一个比较大的数
5 % 5—–>0
6 %5—–>1
7 %5—–>2
……
50—->0
适配器getCount方法逻辑

@Override
        public int getCount() {
            return vpMax;
        }

初始化每个条目的业务逻辑

@Override
        public Object instantiateItem(ViewGroup container, int position) {
            //0.实现无限循环 让 position % 5
            position = position % vpLists.size();
            System.out.println("instantiateItem");
            //1.把想展示的数据取出来
            ImageView iv = vpLists.get(position);
            //2.viewpager 需要自己手动把iv加入到容器(viewpager)里
            container.addView(iv);
            return iv; 
        }

[5]实现viewpager自动滚动逻辑 handler 在onStrat方法里面通过handler发消息实现无限循环

 //当页面可见的时候执行
    @Override
    protected void onStart() {
        //3秒后发送一条消息
        handler.sendEmptyMessageDelayed(10,3000);
        super.onStart();
    }
    //当页面不可见的时候 重写onStop方法
    @Override
    protected void onStop(){
        //移除消息
        handler.removeMessages(10);
        super.onStop();
    }

[6]在handlemessage方法里面让viewpager加载下一页

private  Handler handler = new Handler(){
        //当使用handler发送消息的时候执行 处理消息
        @Override
        public void handleMessage(Message msg) {
            //显示viewpager的下一页
            vp.setCurrentItem(vp.getCurrentItem()+1);
            handler.sendEmptyMessageDelayed(10,3000);
        }
    };
查看评论
    个人资料
    持之以恒
    等级:
    访问量: 6075
    积分: 285
    排名: 27万+
    文章存档
    最新评论