ViewFlipper实现滚动布局
来源:互联网 发布:sql server 导出数据库 编辑:程序博客网 时间:2024/06/08 18:28
今天在git上面看到了一个 仿淘宝头条的滚动效果,就看了下源码
大家要养成看源码的好习惯,才能更好的理解
然后自己照着写了下 然后顺便看了下 ViewFlipper 这个类
先上效果图吧: 最后分析的有 系统的这个类中的方法 的执行 (大神略过,菜鸟请贴完代码后往下看.....)
自定义类继承 ViewFlipper
package com.dreamlive.upmarqueeview;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.view.animation.Animation;import android.view.animation.AnimationUtils;import android.widget.ViewFlipper;import java.util.List;/** * 仿淘宝首页的 淘宝头条滚动的自定义View * * Created by xi on 2016/11/28. */public class UPMarqueeView extends ViewFlipper { private Context mContext; private boolean isSetAnimDuration = false; /** * 要多久 烙下一个视图 不搞的话 默认是3000 * 意思就是 切换的间隔 */ private int interval = 2000; /** * 动画时间 * 因为设置的是 进入 和 出去 的动画为 透明 和 位移 */ private int animDuration = 500; public UPMarqueeView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); } private void init(Context context, AttributeSet attrs, int defStyleAttr) { this.mContext = context; // 设置区间间隔 setFlipInterval(interval); Animation animIn = AnimationUtils.loadAnimation(mContext, R.anim.anim_marquee_in); if (isSetAnimDuration) animIn.setDuration(animDuration); setInAnimation(animIn); Animation animOut = AnimationUtils.loadAnimation(mContext, R.anim.anim_marquee_out); if (isSetAnimDuration) animOut.setDuration(animDuration); setOutAnimation(animOut); } /** * 设置循环滚动的View数组 * * @param views */ public void setViews(final List<View> views) { if (views == null || views.size() == 0) return; removeAllViews(); for ( int i = 0; i < views.size(); i++) { final int position=i; //设置监听回调 views.get(i).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onItemClickListener != null) { onItemClickListener.onItemClick(position, views.get(position)); } } }); addView(views.get(i)); } startFlipping(); } /** * 设置动画时长 默认为500ms * @param ms */ public void setAnimDuration(int ms){ this.animDuration = ms; } /** * 设置间隔时长区间 这里默认给的2s 系统源码里面默认给的3s * @param interval */ public void setInterval(int interval) { this.interval = interval; } /** * 点击 */ private OnItemClickListener onItemClickListener; /** * 设置监听接口 * @param onItemClickListener */ public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } /** * item_view的接口 */ public interface OnItemClickListener { void onItemClick(int position, View view); }}
使用:
1.xml 布局
<?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="horizontal"> <RelativeLayout android:layout_width="match_parent" android:layout_height="80dp"> <TextView android:id="@+id/tbtv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:layout_centerVertical="true" android:textSize="22sp" android:textColor="@color/red" android:text="@string/taobao" /> <com.dreamlive.upmarqueeview.UPMarqueeView android:id="@+id/upview1" android:layout_marginLeft="20dp" android:layout_width="match_parent" android:layout_toRightOf="@+id/tbtv" android:layout_centerVertical="true" android:layout_marginTop="10dp" android:layout_height="match_parent"></com.dreamlive.upmarqueeview.UPMarqueeView> </RelativeLayout></LinearLayout>
item_view:
<?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:gravity="center" android:orientation="vertical"> <RelativeLayout android:id="@+id/rl" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/title_tv1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="热议" android:textSize="9sp" android:padding="3dp" android:background="@drawable/textview_border" android:layout_marginRight="6dp" android:textColor="@color/red" /> <TextView android:id="@+id/tv1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/title_tv1" android:ellipsize="end" android:textSize="14sp" android:maxLines="1" /> </RelativeLayout> <RelativeLayout android:id="@+id/rl2" android:layout_width="match_parent" android:layout_marginTop="5dp" android:layout_height="wrap_content"> <TextView android:id="@+id/title_tv2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="热评" android:padding="3dp" android:textSize="9sp" android:background="@drawable/textview_border" android:layout_marginRight="6dp" android:textColor="@color/red" /> <TextView android:id="@+id/tv2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/title_tv2" android:ellipsize="end" android:textSize="14sp" android:maxLines="1" /> </RelativeLayout></LinearLayout>
2.代码
package com.dreamlive.upmarqueeviewdemo;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;import com.dreamlive.upmarqueeview.UPMarqueeView;import java.util.ArrayList;import java.util.List;/** * 仿淘宝首页的 淘宝头条滚动的自定义View * Created by xi on 2016/11/28. */public class MainActivity extends AppCompatActivity { private UPMarqueeView upview1; List<String> data = new ArrayList<>(); List<View> views = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initParam(); initdata(); initView(); } /** * 实例化控件 */ private void initParam() { upview1 = (UPMarqueeView) findViewById(R.id.upview1); } /** * 初始化界面程序 */ private void initView() { setView(); upview1.setViews(views); /** * 设置item_view的监听 */ upview1.setOnItemClickListener(new UPMarqueeView.OnItemClickListener() { @Override public void onItemClick(int position, View view) { Toast.makeText(MainActivity.this, "你点击了第几个items" + position, Toast.LENGTH_SHORT).show(); } }); } /** * 初始化需要循环的View * 为了灵活的使用滚动的View,所以把滚动的内容让用户自定义 * 假如滚动的是三条或者一条,或者是其他,只需要把对应的布局,和这个方法稍微改改就可以了, */ private void setView() { for (int i = 0; i < data.size(); i = i + 2) { final int position = i; //设置滚动的单个布局 LinearLayout moreView = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.item_view, null); //初始化布局的控件 TextView tv1 = (TextView) moreView.findViewById(R.id.tv1); TextView tv2 = (TextView) moreView.findViewById(R.id.tv2); /** * 设置监听 */ moreView.findViewById(R.id.rl).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, position + "你点击了" + data.get(position).toString(), Toast.LENGTH_SHORT).show(); } }); /** * 设置监听 */ moreView.findViewById(R.id.rl2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, position + "你点击了" + data.get(position).toString(), Toast.LENGTH_SHORT).show(); } }); //进行对控件赋值 tv1.setText(data.get(i).toString()); if (data.size() > i + 1) { //因为淘宝那儿是两条数据,但是当数据是奇数时就不需要赋值第二个,所以加了一个判断,还应该把第二个布局给隐藏掉 tv2.setText(data.get(i + 1).toString()); } else { moreView.findViewById(R.id.rl2).setVisibility(View.GONE); } //添加到循环滚动数组里面去 views.add(moreView); } } /** * 初始化数据 */ private void initdata() { data = new ArrayList<>(); data.add("家人给2岁孩子喝这个,孩子智力倒退10岁!!!"); data.add("iPhone8最感人变化成真,必须买买买买!!!!"); data.add("简直是白菜价!日本玩家33万甩卖15万张游戏王卡"); data.add("iPhone7价格曝光了!看完感觉我的腰子有点疼..."); data.add("主人内疚逃命时没带够,回废墟狂挖30小时!");// data.add("竟不是小米乐视!看水抢了骁龙821首发了!!!"); }}
资源文件:
in ----
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="300" android:fromYDelta="100%p" android:toYDelta="0"/> <alpha android:duration="500" android:fromAlpha="0.0" android:toAlpha="1.0"/></set>
out----
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="400" android:fromYDelta="0" android:toYDelta="-100%p"/> <alpha android:duration="500" android:fromAlpha="1.0" android:toAlpha="0.0"/></set>
在系统的 ViewFlipper 类中可以看到这样的代码 也就是一个广播
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (Intent.ACTION_SCREEN_OFF.equals(action)) { mUserPresent = false; updateRunning(); } else if (Intent.ACTION_USER_PRESENT.equals(action)) { mUserPresent = true; updateRunning(false); } }};@Overrideprotected void onAttachedToWindow() { super.onAttachedToWindow(); // Listen for broadcasts related to user-presence final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_USER_PRESENT); // OK, this is gross but needed. This class is supported by the // remote views machanism and as a part of that the remote views // can be inflated by a context for another user without the app // having interact users permission - just for loading resources. // For exmaple, when adding widgets from a user profile to the // home screen. Therefore, we register the receiver as the current // user not the one the context is for. getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(), filter, null, getHandler());// 这个方法是 Context的 注册广播的方法 if (mAutoStart) { // Automatically start when requested startFlipping(); }}和registerReceiver 一样 但它是对于特殊用户使用的 也就是系统使用的
/** * @hide * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) * but for a specific user. This receiver will receiver broadcasts that * are sent to the requested user. It * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} * permission. * * @param receiver The BroadcastReceiver to handle the broadcast. * @param user UserHandle to send the intent to. * @param filter Selects the Intent broadcasts to be received. * @param broadcastPermission String naming a permissions that a * broadcaster must hold in order to send an Intent to you. If null, * no permission is required. * @param scheduler Handler identifying the thread that will receive * the Intent. If null, the main thread of the process will be used. * * @return The first sticky intent found that matches <var>filter</var>, * or null if there are none. * * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) * @see #sendBroadcast * @see #unregisterReceiver */@Nullablepublic abstract Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler);
@Overrideprotected void onDetachedFromWindow() { super.onDetachedFromWindow(); mVisible = false; getContext().unregisterReceiver(mReceiver); updateRunning();}@Overrideprotected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); mVisible = visibility == VISIBLE; updateRunning(false);}
可以去设置 切换间隔 单位为 毫秒
/** * How long to wait before flipping to the next view * * @param milliseconds * time in milliseconds */@android.view.RemotableViewMethodpublic void setFlipInterval(int milliseconds) { mFlipInterval = milliseconds;}
可以去启动和停止
/** * Start a timer to cycle through child views */public void startFlipping() { mStarted = true; updateRunning();}/** * No more flips */public void stopFlipping() { mStarted = false; updateRunning();}
内部用来启动和停止的方法
/** * Internal method to start or stop dispatching flip {@link Message} based * on {@link #mRunning} and {@link #mVisible} state. */private void updateRunning() { updateRunning(true);}/** * Internal method to start or stop dispatching flip {@link Message} based * on {@link #mRunning} and {@link #mVisible} state. * * @param flipNow Determines whether or not to execute the animation now, in * addition to queuing future flips. If omitted, defaults to * true. */private void updateRunning(boolean flipNow) { boolean running = mVisible && mStarted && mUserPresent; if (running != mRunning) { if (running) { showOnly(mWhichChild, flipNow); postDelayed(mFlipRunnable, mFlipInterval); } else { removeCallbacks(mFlipRunnable); } mRunning = running; } if (LOGD) { Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning); }}
可以看到它调用的是 View 的延时方法 而该方法使用的还是 handler 和 队列
/** * <p>Causes the Runnable to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @return true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. * * @see #post * @see #removeCallbacks */public boolean postDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.postDelayed(action, delayMillis); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().postDelayed(action, delayMillis); return true;}
运行的队列
/** * Returns the queue of runnable for this view. * * @return the queue of runnables for this view */private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); } return mRunQueue;}
发送的延时
public class HandlerActionQueue { private HandlerAction[] mActions; private int mCount; public void post(Runnable action) { postDelayed(action, 0); } public void postDelayed(Runnable action, long delayMillis) { final HandlerAction handlerAction = new HandlerAction(action, delayMillis); synchronized (this) { if (mActions == null) { mActions = new HandlerAction[4]; } mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction); mCount++; } }
而系统 移除回调 是把actions 进行匹配遍历 置为null
public void removeCallbacks(Runnable action) { synchronized (this) { final int count = mCount; int j = 0; final HandlerAction[] actions = mActions; for (int i = 0; i < count; i++) { if (actions[i].matches(action)) { // Remove this action by overwriting it within // this loop or nulling it out later. continue; } if (j != i) { // At least one previous entry was removed, so // this one needs to move to the "new" list. actions[j] = actions[i]; } j++; } // The "new" list only has j entries. mCount = j; // Null out any remaining entries. for (; j < count; j++) { actions[j] = null; } }}
此外在这里 还看到了 执行的方法是用的是 handler
public void executeActions(Handler handler) { synchronized (this) { final HandlerAction[] actions = mActions; for (int i = 0, count = mCount; i < count; i++) { final HandlerAction handlerAction = actions[i]; handler.postDelayed(handlerAction.action, handlerAction.delay); } mActions = null; mCount = 0; }}
到此为止了,下班喽~~
0 0
- ViewFlipper实现滚动布局
- 自定义ViewFlipper 实现滚动效果
- 使用ViewFlipper实现屏幕滚动切换动画
- Android:使用ViewFlipper实现上下滚动消息
- ViewFlipper实现上下滚动字幕 三种方法实现
- Android 用ViewFlipper简单实现广告滚动条
- [Android] 使用 ViewFlipper 实现上下循环滚动通知栏
- Android ViewFlipper实现淘宝垂直滚动广告条
- Android ViewFlipper实现多个布局手势切换的效果
- 学习笔记之——使用ViewFlipper实现竖直滚动广告
- Android 原生控件ViewFlipper实现淘宝头条垂直滚动广告条
- 利用ScrollView实现布局自动滚动
- 利用ScrollView实现布局自动滚动
- Android 利用ScrollView实现布局自动滚动
- 利用ScrollView实现布局自动滚动
- 关于Android线性布局与滚动实现
- ViewFlipper实现图片动画
- viewFlipper的基本实现
- UIWindow基础知识详解
- 配置SharePoint 2016开发环境的时候遇到的两个问题
- Hive分区、分桶操作及其比较
- HDU-5744 Keep On Movin(回文串 贪心)
- Cent OS7.2 服务器安装搭架
- ViewFlipper实现滚动布局
- iOS 去掉html标签
- iOS10本地通知UserNotifications快速入门
- 如何严格设置php中session过期时间
- 一个Win32 C++ 动态连接库的模板 — 调用方可管理DLL分配的内存
- DB2存储过程杂谈
- null is not object evaluating ('_this3_selectRow')
- Android 启动另一个引用的方法记录
- js函数参数和返回值