Android 循环滚动的列表-类似弹幕效果
2023-12-29 16:50:00
前言
?很简单
一、第一种
示例:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
public class FlipperLinearLayout extends LinearLayout {
private int MAX_SHOW_ITEM_SIZE = 5;
private IAdapter mIAdapter;
private int mCount;
//最后一个item动画
private Animation mLastOneAnimation;
//其它item动画
private Animation mCommonAnimation;
//数据下标
private int mCurrentIndex;
/**
* 这里动画时间是4秒,所以间隔得大于动画时间
*/
private static final int DEFAULT_INTERVAL = 4000;
private int mFlipInterval = DEFAULT_INTERVAL;
private boolean mAutoStart = false;
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
private boolean mUserPresent = true;
public ViewFlipper(Context context) {
super(context);
init(context);
}
public ViewFlipper(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ViewFlipper(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
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);
}
}
};
@Override
protected 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().registerReceiver(mReceiver, filter);
if (mAutoStart) {
// Automatically start when requested
startFlipping();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateRunning(mVisible);
// updateRunning(false);
}
private boolean isSetting = false;
public void setMaxItem(int max) {
// if (max < 1) {
// max = 4;
// }
if (isSetting) {
return;
}
isSetting = true;
Log.e("item", "setMaxItem: " + max);
this.MAX_SHOW_ITEM_SIZE = max;
requestLayout();
}
private void init(Context context) {
this.setOrientation(LinearLayout.VERTICAL);
}
public void setIAdapter(IAdapter iAdapter) {
this.mIAdapter = iAdapter;
initShowItems();
}
public void startFlipping() {
if (mStarted) return;
mStarted = true;
updateRunning();
}
public void stopFlipping() {
if (!mStarted) return;
mStarted = false;
updateRunning();
}
private void updateRunning() {
updateRunning(true);
}
/**
* Returns true if the child views are flipping.
*/
public boolean isFlipping() {
return mStarted;
}
/**
* Set if this view automatically calls {@link #startFlipping()} when it
* becomes attached to a window.
*/
public void setAutoStart(boolean autoStart) {
mAutoStart = autoStart;
}
/**
* Returns true if this view automatically calls {@link #startFlipping()}
* when it becomes attached to a window.
*/
public boolean isAutoStart() {
return mAutoStart;
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(ViewFlipper.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(ViewFlipper.class.getName());
}
/**
* 初始化childViews
*/
private void initShowItems() {
if (mIAdapter != null) {
mCount = mIAdapter.getCount();
for (int i = 0; i < mCount; i++) {
if (i == MAX_SHOW_ITEM_SIZE) {
break;
}
View convertView = getChildAt(i);
View item = mIAdapter.getItemView(convertView, i);
addView(item, i);
}
}
}
/**
* 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;
System.out.println(" updateRunning running:" + running + " mVisible " + mVisible + " userPresent " + mUserPresent);
if (running != mRunning) {
if (running && (mCount > MAX_SHOW_ITEM_SIZE)) {
showItems(mCurrentIndex++, flipNow);
Message msg = mHandler.obtainMessage(FLIP_MSG);
mHandler.sendMessageDelayed(msg, mFlipInterval);
} else {
mHandler.removeMessages(FLIP_MSG);
}
mRunning = running;
}
}
private void showItems(final int position, boolean animate) {
if (animate && (mLastOneAnimation == null || mCommonAnimation == null)) {
mLastOneAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.lastone_anim);
mCommonAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.common_anim);
}
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.clearAnimation();
int index = position + i;
child = mIAdapter.getItemView(child, (index >= mIAdapter.getCount()) ? (index - mIAdapter.getCount()) : index);
if (animate) {
if (i == childCount - 1) {
child.setAnimation(mLastOneAnimation);
} else {
child.setAnimation(mCommonAnimation);
}
}
child.setVisibility(View.VISIBLE);
}
if (animate) {
mCommonAnimation.startNow();
mLastOneAnimation.startNow();
}
//保证传入的position小于getCount
if (mCurrentIndex >= mIAdapter.getCount()) {
mCurrentIndex = 0;
}
}
private final int FLIP_MSG = 1;
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == FLIP_MSG) {
if (mRunning) {
showItems(mCurrentIndex++, true);
msg = obtainMessage(FLIP_MSG);
sendMessageDelayed(msg, mFlipInterval);
}
}
}
};
public interface IAdapter {
/**
* @param convertView
* @param position
* @return
*/
public View getItemView(View convertView, int position);
/**
* @return 数据count
*/
public int getCount();
}
}
使用方式:
private List<String> marqueeListData = new ArrayList<>();
public void ini(){
for (int i = 0; i < 20; i++) {
marqueeListData.add("暂无数据" + i);
}
intergralRv.post(() -> {
Log.e("TAG", "postitem intergralRv: " + UtilBox.dip2px(60f, getActivity()) + "_---" + intergralRv.getHeight());
intergralRv.setMaxItem(intergralRv.getHeight() / UtilBox.dip2px(60f, getActivity()) + 2);
intergralRv.setIAdapter(this);
flipperIs = true;
intergralRv.startFlipping();
});
}
二、第二种
示例:
import android.animation.Animator;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.util.Pools;
import com.ylj.water.utils.UtilBox;
import java.util.ArrayList;
import java.util.List;
public class AutoLinLayout extends LinearLayout {
//最多显示的个数
private int maxNum = 1;
//view高度
private int itemH = 3;
public AutoLinLayout(Context context) {
super(context);
init(context);
initAnim();
}
public AutoLinLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
initAnim();
}
public AutoLinLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
initAnim();
}
private void init(Context context) {
this.setOrientation(LinearLayout.VERTICAL);
}
LayoutTransition transition;
private void initAnim() {
transition = new LayoutTransition();
//添加动画
ObjectAnimator valueAnimator = ObjectAnimator.ofFloat(null, "alpha", 0, 1);
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//当前展示超过四条,执行删除动画
if (AutoLinLayout.this.getChildCount() == (maxNum - 1)) {
handler.sendEmptyMessage(1);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (AutoLinLayout.this.getChildCount() >= maxNum) {
//动画执行完毕,删除view
handler.sendEmptyMessage(2);
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
transition.setAnimator(LayoutTransition.APPEARING, valueAnimator);
//删除动画
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0, 0);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(null, new PropertyValuesHolder[]{alpha}).setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
transition.setAnimator(LayoutTransition.DISAPPEARING, objectAnimator);
this.setLayoutTransition(transition);
}
private boolean isRun = false;
public void startAuto() {
if (isRun) {
return;
}
if (texts.size() < 1) {
return;
}
isRun = true;
handler.sendEmptyMessage(0);
}
public void startAuto(List<String> data) {
if (isRun) {
return;
}
if (data.size() < 1) return;
texts = data;
textViewPool = new Pools.SimplePool<>(texts.size());
handler.sendEmptyMessage(0);
}
public void setItemHeight(int h) {
this.itemH = h;
}
public void startAuto(List<String> data, int max) {
if (isRun) {
return;
}
if (data.size() < 1) return;
texts = data;
textViewPool = new Pools.SimplePool<>(texts.size());
// maxNum = h / UtilBox.dip2px(itemH, getContext());
maxNum = max;
if (maxNum > 0) {
for (int i = 0; i < (maxNum - 2); i++) {
TextView textView = obtainTextView();
AutoLinLayout.this.addView(textView);
index++;
}
isRun = true;
}
handler.sendEmptyMessage(0);
}
public void stopAuto() {
if (!isRun) {
return;
}
isRun = false;
handler.removeMessages(0);
}
private List<String> texts = new ArrayList<>();
Pools.SimplePool<TextView> textViewPool;
private TextView obtainTextView() {
if (null == textViewPool) return null;
TextView textView = textViewPool.acquire();
if (textView == null) {
textView = new TextView(getContext());
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, UtilBox.dip2px(itemH, getContext())));
textView.setPadding(UtilBox.dip2px(10, getContext()), UtilBox.dip2px(5, getContext()), UtilBox.dip2px(10, getContext()), UtilBox.dip2px(5, getContext()));
textView.setTextColor(0xffffffff);
textView.setMaxLines(1);
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(15);
textView.setTextColor(0xff000000);
textView.setCompoundDrawablePadding(10);
}
textView.setTag(index);
textView.setText(texts.get(index));
return textView;
}
int index = 0;
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@SuppressLint("ResourceAsColor")
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
if (texts.size() < 1) return;
if (!isRun) return;
TextView textView = obtainTextView();
AutoLinLayout.this.addView(textView);
sendEmptyMessageDelayed(0, 2000);
index++;
if (index == texts.size()) {
index = 0;
}
break;
case 1:
if (texts.size() < 1) return;
if (!isRun) return;
//给展示的第一个view增加渐变透明动画
AutoLinLayout.this.getChildAt(0).animate().translationY(-30).alpha(0).setDuration(transition.getDuration(LayoutTransition.APPEARING)).start();
break;
case 2:
if (texts.size() < 1) return;
if (!isRun) return;
//删除顶部view
AutoLinLayout.this.removeViewAt(0);
break;
}
}
};
}
使用方式:
public void init(){
llContainer.setItemHeight(30);
llContainer.post(() -> {
llContainer.startAuto(marqueeListData,llContainer.getHeight()/UtilBox.dip2px(30, getActivity()));
flipperIs = true;
});
}
@Override
public void onResume() {
super.onResume();
if (flipperIs) {
llContainer.startAuto();
}
}
@Override
public void onPause() {
super.onPause();
llContainer.stopAuto();
}
lastone_anim和common_anim资源文件
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_in_left.xml
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="2000" />
<translate android:fromYDelta="0" android:toYDelta="-100%"
android:duration="2000"
android:startOffset="2000"/>
</set>
<?xml version="1.0" encoding="utf-8"?><!--
/* //device/apps/common/res/anim/slide_in_left.xml
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<alpha
android:fromAlpha="1.0"
android:toAlpha="1.0"
android:duration="2000" />
<translate
android:fromYDelta="0"
android:toYDelta="-100%"
android:duration="2000"
android:startOffset="2000" />
</set>
?总结
实现的很是简单,由于时间的原因,很多地方也不够完善,
仅供参考。
文章来源:https://blog.csdn.net/weixin_41620505/article/details/135291818
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!