android Message Vertical Rolling Rotary Control
Similar to Taobao homepage, the control of vertical scrolling of messages is very simple to implement. There are many examples on the Internet, but most of the examples on the Internet are using android's native control ViewFlipper, which I also used. Finally, I found that it was very pitiful and had many problems.
Let's start with the problems encountered in using ViewFliper. The first is that ViewFlipper sometimes does not scroll automatically after the screen is locked and then opened. Looking at the source code of ViewFlipper, you can see that a Broadcast Receiver is registered in ViewFlipper to receive the system lock screen and open-screen messages, and to control one of the boolean variables mUserPresent, which directly determines that ViewFlipper is a boolean variable. Whether it can scroll automatically or not, but my test found that sometimes after the mobile phone lock screen is opened again, the viewFlipper radio receiver does not receive the open-screen broadcast message at all, resulting in the state of variables unchanged, which leads to the view Flipper can not scroll automatically.
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);
}
}
};
...
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);
}
}
And I also encountered the problem of displaying ghosts after switching back and forth in Fragment using ViewFlipper, but this problem can be solved by calling stopFlipping and stopFlipping functions of ViewFlipper in switching. However, in doing so, you will still find a problem: the first message displayed after the cut-back is with an entry animation, but the above is blank!
Looking at the startFlipping source code of ViewFlipper, you can see that startFlipping calls the updateRunning(true) method, which in turn calls the showOnly(childId, flipNow) method of the parent ViewAnimator. The showOnly method shows which child view is based on the incoming parameter childId, and decides whether to play animation according to flipNow. Unfortunately, it calls startFlipping by default. The parameter is to play the animation, which results in showing the entrance animation when displaying the first item, but why the above is blank, I haven't understood for the time being, the God knows, please let me know! I can't thank you enough for the side dish here.
public void startFlipping() {
mStarted = true;
updateRunning();
}
private void updateRunning() {
updateRunning(true);
}
Since ViewFlipper does not meet the requirements, then write a MyViewFlipper by yourself. Refer to the source code of ViewFlipper, change part of the code, remove the part of broadcasting registration, and then transfer the startFlipping into the non-playing animation. The effect is as follows:
Be accomplished!
Following is the code
- 1,TextViewSwitcher
package com.example.lee.rollingtextview.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.AnimRes;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ViewAnimator;
import com.example.lee.rollingtextview.R;
import com.example.lee.rollingtextview.RollingTextAdapter;
/**
* Created by lihuayong on 18/7/20.
* Code reference ViewFlipper source code
* Modified part of the source code, such as no longer receiving mobile phone lock screen and open-screen broadcasting, need to call the api manual settings for automatic playback
*
*/
public class TextViewSwitcher extends ViewAnimator {
private static final String TAG = "TextViewSwitcher";
private static final int DEFAULT_FLIP_DURATION = 500;
private static final int DEFAULT_FLIP_INTERVAL = 3000;
private static final int DEFAULT_IN_ANIMATION = R.anim.rolling_text_in;
private static final int DEFAULT_OUT_ANIMATION = R.anim.rolling_text_out;
/**
* Time interval of switching
* */
private int mFlipInterval = DEFAULT_FLIP_INTERVAL;
/**
* Time interval of animation switching
*/
private int mFlipDuration = DEFAULT_FLIP_DURATION;
@AnimRes
private int mInAnimation = DEFAULT_IN_ANIMATION;
@AnimRes
private int mOutAnimation = DEFAULT_OUT_ANIMATION;
private boolean mAutoStart = false;
private boolean mVisible = false;
private boolean mStarted = false;
private boolean mRunning = false;
private RollingTextAdapter mAdapter;
private OnItemClickListener mListener;
private int mCurItem = 0;
public TextViewSwitcher(Context context) {
this(context,null);
}
public TextViewSwitcher(Context context, AttributeSet attrs) {
super(context, attrs);
parseConfig(context,attrs);
init(context);
}
private void parseConfig(Context context, @Nullable AttributeSet attrs){
if(null != attrs) {
/** get set from xml files */
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextViewSwitcher);
mFlipDuration = a.getInteger(R.styleable.TextViewSwitcher_flipDuration, DEFAULT_FLIP_DURATION);
mFlipInterval = a.getInteger(R.styleable.TextViewSwitcher_flipInterval, DEFAULT_FLIP_INTERVAL);
mAutoStart = a.getBoolean(R.styleable.TextViewSwitcher_autoStart, false);
mInAnimation = a.getResourceId(R.styleable.TextViewSwitcher_inAnimation, DEFAULT_IN_ANIMATION);
mOutAnimation = a.getResourceId(R.styleable.TextViewSwitcher_outAnimation, DEFAULT_OUT_ANIMATION);
a.recycle();
}
}
private void init(Context context){
Animation animIn = AnimationUtils.loadAnimation(context, mInAnimation);
setInAnimation(animIn);
Animation animOut = AnimationUtils.loadAnimation(context, mOutAnimation);
setOutAnimation(animOut);
setFlipInterval(mFlipInterval);
setFlipDuration(mFlipDuration);
setAutoStart(mAutoStart);
}
/**
* load subview
*/
private void start(){
if(this.mAdapter == null||this.mAdapter.getCount() <= 0){
return;
}
removeAllViews();
//auto start flipping while data size >= 2
if(this.mAdapter.getCount() >2)
setAutoStart(true);
else
setAutoStart(false);
for(int i =0;i<this.mAdapter.getCount();i= i+2){
View view = this.mAdapter.getView(getContext(),getCurrentView(),i);
addView(view);
if(mListener != null){
view.setTag(i);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
mListener.onClick((int)view.getTag());
}
});
}
}
}
/**
* set view adapter
* @param adapter the adapter
*/
public void setAdapter(RollingTextAdapter adapter){
this.mAdapter = adapter;
start();
}
public void setOnItemClickListener(OnItemClickListener listener){
this.mListener = listener;
start();
}
/**
* Update view display
*/
private void updateRunning(){
updateRunning(true);
}
/**
* Only child views of the current id are displayed in the layout, and no other views are displayed.
* @param childIndex id of child view
* @param animate Do you want to show animation?
*/
void showOnlyOneChild(int childIndex, boolean animate) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (i == childIndex) {
if (animate && getInAnimation() != null) {
child.startAnimation(getInAnimation());
}
child.setVisibility(View.VISIBLE);
} else {
if (animate && getOutAnimation() != null && child.getVisibility() == View.VISIBLE) {
child.startAnimation(getOutAnimation());
} else if (child.getAnimation() == getInAnimation())
child.clearAnimation();
child.setVisibility(View.GONE);
}
}
}
private void updateRunning(boolean flipNow){
boolean running = mVisible && mStarted ;
if (running != mRunning) {
if (running) {
showOnlyOneChild(getDisplayedChild(),flipNow);
postDelayed(mRollRunnable, mFlipInterval);
} else {
removeCallbacks(mRollRunnable);
}
mRunning = running;
}
}
@Override
public void removeAllViews() {
super.removeAllViews();
mCurItem = 0;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Log.i(TAG,"onAttachedToWindow");
if(mAutoStart){
startFlipping();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.i(TAG,"onDetachedFromWindow");
mVisible = false;
updateRunning();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
Log.i(TAG,"onWindowVisibilityChanged "+(visibility == VISIBLE ? "VISIBLE":"INVISIBLE"));
mVisible = visibility == VISIBLE;
updateRunning(false);
}
/**
* Set if this view automatically calls {@link #startFlipping()} when it
* becomes attached to a window.
*/
public void setAutoStart(boolean autoStart){
this.mAutoStart = autoStart;
}
/**
* Returns true if this view automatically calls {@link #startFlipping()}
* when it becomes attached to a window.
*/
public boolean isAutoStart(){return mAutoStart;}
/**
* Start a timer to cycle through child views
* without show animation.
*/
public void startFlipping() {
mStarted = true;
updateRunning(false);
}
/**
* No more flips
*/
public void stopFlipping() {
mStarted = false;
updateRunning();
}
/**
* Returns true if the child views are flipping.
*/
public boolean isFlipping() {
return mStarted;
}
/**
* How long to wait before flipping to the next view
* @param milliseconds time in milliseconds
*/
public void setFlipInterval(int milliseconds) {
this.mFlipInterval = milliseconds;
}
/**
* How long to finish flipping in/out animation
* @param milliseconds time in milliseconds
*/
public void setFlipDuration(int milliseconds){
this.mFlipDuration = milliseconds;
getInAnimation().setDuration(mFlipDuration);
getOutAnimation().setDuration(mFlipDuration);
}
private final Runnable mRollRunnable = new Runnable() {
@Override
public void run() {
if (mRunning) {
mCurItem = mCurItem == getChildCount()-1 ? 0 : mCurItem+1;
showNext();
postDelayed(mRollRunnable, mFlipInterval);
Log.i(TAG,"child :" +getDisplayedChild()+" is "+(getChildAt(getDisplayedChild()).getVisibility() == VISIBLE ? "VISIBLE":"INVISIBLE"));
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
+ ", mRunning=" + mRunning);
}
}
};
public interface OnItemClickListener{
void onClick(int position);
}
}
- 2 RollingTextAdapter
package com.example.lee.rollingtextview;
import android.content.Context;
import android.view.View;
public abstract class RollingTextAdapter {
public abstract int getCount();
public abstract View getView(Context context, View contentView, int position);
}
- 3,RollTextItem
package com.example.lee.rollingtextview.view;
public class RollTextItem {
public int getImgId() {
return imgId;
}
public void setImgId(int imgId) {
this.imgId = imgId;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
private int imgId;
private String msg;
private int textColor;
public RollTextItem(String msg, int imgId){
this(msg,imgId, android.R.color.holo_blue_light);
}
public RollTextItem(String msg, int imgId, int textColor){
this.msg = msg;
this.imgId = imgId;
this.textColor = textColor;
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
}
}
- 4,MainActivity
package com.example.lee.rollingtextview;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.example.lee.rollingtextview.view.RollTextItem;
import com.example.lee.rollingtextview.view.TextViewSwitcher;
import com.example.lee.rollingtextview.view.TextViewSwitcher.OnItemClickListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
private TextViewSwitcher rollingText;
private Random randomColor;
private List<RollTextItem> data = new ArrayList<>();
int[] textColor = {android.R.color.holo_blue_light,android.R.color.holo_green_light,android.R.color.holo_orange_light,
android.R.color.holo_purple,android.R.color.holo_red_light,android.R.color.black,android.R.color.holo_orange_dark};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rollingText = findViewById(R.id.rolltext);
randomColor = new Random();
final Button btnCtrl = findViewById(R.id.btn_control);
btnCtrl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(btnCtrl.getText().equals("Stop rolling")){
btnCtrl.setText("Start rolling");
rollingText.stopFlipping();
}else{
btnCtrl.setText("Stop rolling");
rollingText.startFlipping();
}
}
});
initData();
rollingText.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onClick(int position) {
Toast.makeText(MainActivity.this,"item Be clicked"+position,Toast.LENGTH_SHORT).show();
}
});
rollingText.setAdapter(new RollingTextAdapter() {
@Override
public int getCount() {
return data.size()/2;
}
@SuppressLint("ResourceAsColor")
@Override
public View getView(Context context, View contentView, int position) {
View view = View.inflate(context,R.layout.item_layout,null);
((TextView)view.findViewById(R.id.tv_1)).setText(data.get(position).getMsg());
int randomId = randomColor.nextInt(100);
((TextView)view.findViewById(R.id.tv_1)).setTextColor(getResources().getColor(textColor[randomId%7]));
((TextView)view.findViewById(R.id.tv_2)).setText(data.get((position+1)%data.size()).getMsg());
randomId = randomColor.nextInt(100);
((TextView)view.findViewById(R.id.tv_2)).setTextColor(getResources().getColor(textColor[randomId%7]));
return view;
}
});
}
private void initData() {
data.add(new RollTextItem("When you walk into this playground",R.drawable.ic_launcher_foreground,textColor[0]));
data.add(new RollTextItem("All Dreams and Thoughts on the Back",R.drawable.ic_launcher_foreground,textColor[1]));
data.add(new RollTextItem("All kinds of makeup on all kinds of faces",R.drawable.ic_launcher_foreground,textColor[2]));
data.add(new RollTextItem("Nobody remembers what you look like.",R.drawable.ic_launcher_foreground,textColor[3]));
data.add(new RollTextItem("Three tours of wine have passed you around the corner",R.drawable.ic_launcher_foreground,textColor[4]));
data.add(new RollTextItem("Stubborn singing bitter songs",R.drawable.ic_launcher_foreground,textColor[5]));
data.add(new RollTextItem("Listen to it drowned in the noise",R.drawable.ic_launcher_foreground,textColor[6]));
data.add(new RollTextItem("You pick up your glass and say to yourself",R.drawable.ic_launcher_foreground,textColor[1]));
data.add(new RollTextItem("A glass of sunrise and a glass of moonlight",R.drawable.ic_launcher_foreground,textColor[2]));
data.add(new RollTextItem("Wake up my yearning and gentle the cold window",R.drawable.ic_launcher_foreground,textColor[3]));
data.add(new RollTextItem("So you can fly against the wind without looking back.",R.drawable.ic_launcher_foreground,textColor[4]));
data.add(new RollTextItem("Don't be afraid of rain in your heart and frost in your eyes",R.drawable.ic_launcher_foreground,textColor[5]));
}
}
- 4. Entry and exit animations roll_text_in and roll_text_out
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="1000"
android:fromYDelta="100%p"
android:toYDelta="0"/>
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="1000"
android:fromYDelta="0"
android:toYDelta="-100%p"/>
</set>
- 5,item_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_1"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/holo_purple"
android:textSize="20sp"
android:text="hello world"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_2"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/holo_purple"
android:textSize="20sp"
android:text="hello world"/>
</LinearLayout>
</LinearLayout>
Let's start with that. I just started blogging, XD. It refers to some methods on the Internet. If the links are not saved, they will not be listed here one by one. Please forgive me.
Engineering demo link Download address