今天看了淘宝头条的的滚动,感觉用户体验非常好,然后在就github上找到了一个,
效果图如下:
实现方式通过MarqueeFactory来提供各种样式的跑马灯View, 支持自定义跑马灯ItemView。
1.首先在build.gradle中增加:
compile ‘com.gongwen:marqueelibrary:1.0.4’
2.MainActivity的xml文件中:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.administrator.marqueeviewdemo.MainActivity">
<com.gongwen.marqueen.MarqueeView
android:id="@+id/marqueeView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:background="#88dddddd"/>
<com.gongwen.marqueen.MarqueeView
android:id="@+id/marqueeView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:background="#88dddddd"
app:marqueeAnimDuration="2000"
app:marqueeAnimIn="@anim/right_in"
app:marqueeAnimOut="@anim/left_out"
app:marqueeInterval="2500"/>
<com.gongwen.marqueen.MarqueeView
android:id="@+id/marqueeView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:background="#88dddddd"/>
<com.gongwen.marqueen.MarqueeView
android:id="@+id/marqueeView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:background="#88dddddd"/>
<com.gongwen.marqueen.MarqueeView
android:id="@+id/marqueeView5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:background="#88dddddd"/>
</LinearLayout>
3.MainActivity中:
public class MainActivity extends AppCompatActivity {
private MarqueeView marqueeView1, marqueeView2, marqueeView3, marqueeView4, marqueeView5;
private List<String> list;
private WeakHandler mHandler = new WeakHandler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = new ArrayList<>();
list.add("人生若只如初见,何事秋风悲画扇");
list.add("等闲变却故人心,却道故人心易变");
list.add("骊山语罢清宵半,泪雨零铃终不怨");
list.add("何如薄幸锦衣郎,比翼连枝当日愿");
marqueeView1 = (MarqueeView) findViewById(R.id.marqueeView1);
marqueeView2 = (MarqueeView) findViewById(R.id.marqueeView2);
marqueeView3 = (MarqueeView) findViewById(R.id.marqueeView3);
marqueeView4 = (MarqueeView) findViewById(R.id.marqueeView4);
marqueeView5 = (MarqueeView) findViewById(R.id.marqueeView5);
//文字向上翻转
MarqueeFactory<TextView, String> marqueeFactory1 = new NoticeMF(this);
//MarqueeView设置Factory
marqueeView1.setMarqueeFactory(marqueeFactory1);
//开始翻转
marqueeView1.startFlipping();
//设置item的监听
marqueeFactory1.setOnItemClickListener(new MarqueeFactory.OnItemClickListener<TextView, String>() {
@Override
public void onItemClickListener(MarqueeFactory.ViewHolder<TextView, String> holder) {
Toast.makeText(MainActivity.this, holder.data, Toast.LENGTH_SHORT).show();
}
});
//设置数据
marqueeFactory1.setData(list);
//文字向左翻转
final MarqueeFactory<TextView, String> marqueeFactory2 = new NoticeMF(this);
//MarqueeView设置Factory
marqueeView2.setMarqueeFactory(marqueeFactory2);
//开始翻转
marqueeView2.startFlipping();
//设置item的监听
marqueeFactory2.setOnItemClickListener(new MarqueeFactory.OnItemClickListener<TextView, String>() {
@Override
public void onItemClickListener(MarqueeFactory.ViewHolder<TextView, String> holder) {
Toast.makeText(MainActivity.this, holder.data, Toast.LENGTH_SHORT).show();
}
});
//设置数据
marqueeFactory2.setData(list);
//文字向右翻转
MarqueeFactory<TextView, String> marqueeFactory3 = new NoticeMF(this);
//设置监听
marqueeFactory3.setOnItemClickListener(new MarqueeFactory.OnItemClickListener<TextView, String>() {
@Override
public void onItemClickListener(MarqueeFactory.ViewHolder<TextView, String> holder) {
Toast.makeText(MainActivity.this, holder.data, Toast.LENGTH_SHORT).show();
}
});
//设置数据
marqueeFactory3.setData(list);
//MarqueeView设置Factory
marqueeView3.setMarqueeFactory(marqueeFactory3);
//设置进入退出的动画
marqueeView3.setAnimInAndOut(R.anim.left_in, R.anim.right_out);
//动画执行时间
marqueeView3.setAnimDuration(2000);
//翻页时间间隔
marqueeView3.setInterval(2500);
marqueeView3.setAnimateFirstView(true);
//直接调用startFlipping,setAnimateFirstView并没有生效
//marqueeView3.startFlipping();
mHandler.post(new Runnable() {
@Override
public void run() {
//开始翻转
marqueeView3.startFlipping();
}
});
//文字向下翻转
MarqueeFactory<TextView, String> marqueeFactory4 = new NoticeMF(this);
//设置监听
marqueeFactory4.setOnItemClickListener(new MarqueeFactory.OnItemClickListener<TextView, String>() {
@Override
public void onItemClickListener(MarqueeFactory.ViewHolder<TextView, String> holder) {
Toast.makeText(MainActivity.this, holder.data, Toast.LENGTH_SHORT).show();
}
});
//设置数据
marqueeFactory4.setData(list);
//设置进入退出的动画
marqueeView4.setAnimInAndOut(R.anim.top_in, R.anim.bottom_out);
//MarqueeView设置Factory
marqueeView4.setMarqueeFactory(marqueeFactory4);
//开始翻转
marqueeView4.startFlipping();
//设置多行布局
List<MyData> dataList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
dataList.add(new MyData("标题 " + i, "内容 " + i, "时间 " + i));
}
MarqueeFactory<RelativeLayout, MyData> marqueeFactory5 = new ComplexViewMF(this);
marqueeFactory5.setData(dataList);
marqueeView5.setAnimInAndOut(R.anim.top_in, R.anim.bottom_out);
//MarqueeView设置Factory
marqueeView5.setMarqueeFactory(marqueeFactory5);
//开始翻转
marqueeView5.startFlipping();
}
@Override
protected void onStart() {
super.onStart();
marqueeView1.startFlipping();
marqueeView2.startFlipping();
marqueeView3.startFlipping();
marqueeView4.startFlipping();
marqueeView5.startFlipping();
}
@Override
protected void onStop() {
super.onStop();
marqueeView1.stopFlipping();
marqueeView2.stopFlipping();
marqueeView3.stopFlipping();
marqueeView4.stopFlipping();
marqueeView5.stopFlipping();
}
}
4.NoticeMF中:
public class NoticeMF extends MarqueeFactory<TextView, String> {
private LayoutInflater inflater;
public NoticeMF(Context mContext) {
super(mContext);
inflater = LayoutInflater.from(mContext);
}
@Override
public TextView generateMarqueeItemView(String data) {
TextView mView = (TextView) inflater.inflate(R.layout.notice_item, null);
mView.setText(data);
return mView;
}
}
5.notice_item中:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="1"
android:padding="15dp"
android:textSize="15sp" />
6.MyData中:
public class MyData {
private String title;
private String body;
private String time;
public MyData(String title, String body, String time) {
this.title = title;
this.body = body;
this.time = time;
}
public MyData() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
7.ComplexViewMF中:
public class ComplexViewMF extends MarqueeFactory<RelativeLayout, MyData> {
private LayoutInflater inflater;
public ComplexViewMF(Context mContext) {
super(mContext);
inflater = LayoutInflater.from(mContext);
}
@Override
public RelativeLayout generateMarqueeItemView(MyData data) {
RelativeLayout mView = (RelativeLayout) inflater.inflate(R.layout.complex_view, null);
((TextView) mView.findViewById(R.id.title)).setText(data.getTitle());
((TextView) mView.findViewById(R.id.body)).setText(data.getBody());
((TextView) mView.findViewById(R.id.time)).setText(data.getTime());
return mView;
}
}
8.complex_view中:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题" />
<TextView
android:id="@+id/body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_marginTop="10dp"
android:text="副标题" />
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="2016-12-20 18:18" />
</RelativeLayout>
9.WeakHandler中:
/*
* Copyright (c) 2014 Badoo Trading Limited
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Portions of documentation in this code are modifications based on work created and
* shared by Android Open Source Project and used according to terms described in the
* Apache License, Version 2.0
*/
package com.example.administrator.marqueeviewdemo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import java.lang.ref.WeakReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Memory safer implementation of android.os.Handler
* <p/>
* Original implementation of Handlers always keeps hard reference to handler in queue of execution.
* If you create anonymous handler and post delayed message into it, it will keep all parent class
* for that time in memory even if it could be cleaned.
* <p/>
* This implementation is trickier, it will keep WeakReferences to runnables and messages,
* and GC could collect them once WeakHandler instance is not referenced any more
* <p/>
*
* @see Handler
*
* Created by Dmytro Voronkevych on 17/06/2014.
*/
@SuppressWarnings("unused")
public class WeakHandler {
private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory
private final ExecHandler mExec;
private Lock mLock = new ReentrantLock();
@SuppressWarnings("ConstantConditions")
@VisibleForTesting
final ChainedRef mRunnables = new ChainedRef(mLock, null);
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public WeakHandler() {
mCallback = null;
mExec = new ExecHandler();
}
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public WeakHandler(@Nullable Handler.Callback callback) {
mCallback = callback; // Hard referencing body
mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler
}
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public WeakHandler(@NonNull Looper looper) {
mCallback = null;
mExec = new ExecHandler(looper);
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
*/
public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) {
mCallback = callback;
mExec = new ExecHandler(looper, new WeakReference<>(callback));
}
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns 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.
*/
public final boolean post(@NonNull Runnable r) {
return mExec.post(wrapRunnable(r));
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns 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.
*/
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return mExec.postAtTime(wrapRunnable(r), uptimeMillis);
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns 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 android.os.SystemClock#uptimeMillis
*/
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis);
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the thread to which this handler
* is attached.
*
* @param r The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
*
* @return Returns 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.
*/
public final boolean postDelayed(Runnable r, long delayMillis) {
return mExec.postDelayed(wrapRunnable(r), delayMillis);
}
/**
* Posts a message to an object that implements Runnable.
* Causes the Runnable r to executed on the next iteration through the
* message queue. The runnable will be run on the thread to which this
* handler is attached.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean postAtFrontOfQueue(Runnable r) {
return mExec.postAtFrontOfQueue(wrapRunnable(r));
}
/**
* Remove any pending posts of Runnable r that are in the message queue.
*/
public final void removeCallbacks(Runnable r) {
final WeakRunnable runnable = mRunnables.remove(r);
if (runnable != null) {
mExec.removeCallbacks(runnable);
}
}
/**
* Remove any pending posts of Runnable <var>r</var> with Object
* <var>token</var> that are in the message queue. If <var>token</var> is null,
* all callbacks will be removed.
*/
public final void removeCallbacks(Runnable r, Object token) {
final WeakRunnable runnable = mRunnables.remove(r);
if (runnable != null) {
mExec.removeCallbacks(runnable, token);
}
}
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in callback,
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg) {
return mExec.sendMessage(msg);
}
/**
* Sends a Message containing only the what value.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessage(int what) {
return mExec.sendEmptyMessage(what);
}
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
return mExec.sendEmptyMessageDelayed(what, delayMillis);
}
/**
* Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
return mExec.sendEmptyMessageAtTime(what, uptimeMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* callback, in the thread attached to this handler.
*
* @return Returns true if the message 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 message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
return mExec.sendMessageDelayed(msg, delayMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* You will receive it in callback, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message 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 message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
return mExec.sendMessageAtTime(msg, uptimeMillis);
}
/**
* Enqueue a message at the front of the message queue, to be processed on
* the next iteration of the message loop. You will receive it in
* callback, in the thread attached to this handler.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessageAtFrontOfQueue(Message msg) {
return mExec.sendMessageAtFrontOfQueue(msg);
}
/**
* Remove any pending posts of messages with code 'what' that are in the
* message queue.
*/
public final void removeMessages(int what) {
mExec.removeMessages(what);
}
/**
* Remove any pending posts of messages with code 'what' and whose obj is
* 'object' that are in the message queue. If <var>object</var> is null,
* all messages will be removed.
*/
public final void removeMessages(int what, Object object) {
mExec.removeMessages(what, object);
}
/**
* Remove any pending posts of callbacks and sent messages whose
* <var>obj</var> is <var>token</var>. If <var>token</var> is null,
* all callbacks and messages will be removed.
*/
public final void removeCallbacksAndMessages(Object token) {
mExec.removeCallbacksAndMessages(token);
}
/**
* Check if there are any pending posts of messages with code 'what' in
* the message queue.
*/
public final boolean hasMessages(int what) {
return mExec.hasMessages(what);
}
/**
* Check if there are any pending posts of messages with code 'what' and
* whose obj is 'object' in the message queue.
*/
public final boolean hasMessages(int what, Object object) {
return mExec.hasMessages(what, object);
}
public final Looper getLooper() {
return mExec.getLooper();
}
private WeakRunnable wrapRunnable(@NonNull Runnable r) {
//noinspection ConstantConditions
if (r == null) {
throw new NullPointerException("Runnable can't be null");
}
final ChainedRef hardRef = new ChainedRef(mLock, r);
mRunnables.insertAfter(hardRef);
return hardRef.wrapper;
}
private static class ExecHandler extends Handler {
private final WeakReference<Callback> mCallback;
ExecHandler() {
mCallback = null;
}
ExecHandler(WeakReference<Callback> callback) {
mCallback = callback;
}
ExecHandler(Looper looper) {
super(looper);
mCallback = null;
}
ExecHandler(Looper looper, WeakReference<Callback> callback) {
super(looper);
mCallback = callback;
}
@Override
public void handleMessage(@NonNull Message msg) {
if (mCallback == null) {
return;
}
final Callback callback = mCallback.get();
if (callback == null) { // Already disposed
return;
}
callback.handleMessage(msg);
}
}
static class WeakRunnable implements Runnable {
private final WeakReference<Runnable> mDelegate;
private final WeakReference<ChainedRef> mReference;
WeakRunnable(WeakReference<Runnable> delegate, WeakReference<ChainedRef> reference) {
mDelegate = delegate;
mReference = reference;
}
@Override
public void run() {
final Runnable delegate = mDelegate.get();
final ChainedRef reference = mReference.get();
if (reference != null) {
reference.remove();
}
if (delegate != null) {
delegate.run();
}
}
}
static class ChainedRef {
@Nullable
ChainedRef next;
@Nullable
ChainedRef prev;
@NonNull
final Runnable runnable;
@NonNull
final WeakRunnable wrapper;
@NonNull
Lock lock;
public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) {
this.runnable = r;
this.lock = lock;
this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this));
}
public WeakRunnable remove() {
lock.lock();
try {
if (prev != null) {
prev.next = next;
}
if (next != null) {
next.prev = prev;
}
prev = null;
next = null;
} finally {
lock.unlock();
}
return wrapper;
}
public void insertAfter(@NonNull ChainedRef candidate) {
lock.lock();
try {
if (this.next != null) {
this.next.prev = candidate;
}
candidate.next = this.next;
this.next = candidate;
candidate.prev = this;
} finally {
lock.unlock();
}
}
@Nullable
public WeakRunnable remove(Runnable obj) {
lock.lock();
try {
ChainedRef curr = this.next; // Skipping head
while (curr != null) {
if (curr.runnable == obj) { // We do comparison exactly how Handler does inside
return curr.remove();
}
curr = curr.next;
}
} finally {
lock.unlock();
}
return null;
}
}
}
10.anim文件中:
①bottom_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="100%p"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"/>
</set>
②left_in.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-100%p"
android:toXDelta="0.0"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
</set>
③left_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="-100%p"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"/>
</set>
④right_in.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100%p"
android:toXDelta="0"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
</set>
⑤right_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="100%p"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"/>
</set>
⑥top_in.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="-100%p"
android:toYDelta="0.0"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
</set>
11.demo下载地址:
本人菜鸟一个,有什么不对的地方希望大家指出评论,大神勿喷,希望大家一起学习进步!