先上效果图(点击图片可以跳转相应webview)
1.添加Glide依赖
implementation 'com.github.bumptech.glide:glide:3.7.0'
2.创建工具类
2.1创建CircleFlowIndicator工具类
package com.example.test.util;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import com.example.test.R;
public class CircleFlowIndicator extends View implements FlowIndicator,
AnimationListener {
private static final int STYLE_STROKE = 0;
private static final int STYLE_FILL = 1;
private float radius = 4;
private float circleSeparation = 2*radius+radius;
private float activeRadius = 0.5f;
private int fadeOutTime = 0;
private final Paint mPaintInactive = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mPaintActive = new Paint(Paint.ANTI_ALIAS_FLAG);
private ViewFlow viewFlow;
private int currentScroll = 0;
private int flowWidth = 0;
private FadeTimer timer;
public AnimationListener animationListener = this;
private Animation animation;
private boolean mCentered = false;
/**
* Default constructor
*
* @param context
*/
public CircleFlowIndicator(Context context) {
super(context);
initColors(0xFFFFFFFF, 0xFFFFFFFF, STYLE_FILL, STYLE_STROKE);
}
/**
* The contructor used with an inflater
*
* @param context
* @param attrs
*/
public CircleFlowIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
// Retrieve styles attributs
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CircleFlowIndicator);
// Gets the inactive circle type, defaulting to "fill"
int activeType = a.getInt(R.styleable.CircleFlowIndicator_activeType,
STYLE_FILL);
int activeDefaultColor = 0xFFFFFFFF;
// Get a custom inactive color if there is one
int activeColor = a
.getColor(R.styleable.CircleFlowIndicator_activeColor,
activeDefaultColor);
// Gets the inactive circle type, defaulting to "stroke"
int inactiveType = a.getInt(
R.styleable.CircleFlowIndicator_inactiveType, STYLE_STROKE);
int inactiveDefaultColor = 0x44FFFFFF;
// Get a custom inactive color if there is one
int inactiveColor = a.getColor(
R.styleable.CircleFlowIndicator_inactiveColor,
inactiveDefaultColor);
// Retrieve the radius
radius = a.getDimension(R.styleable.CircleFlowIndicator_radius, 4.0f);
circleSeparation = a.getDimension(R.styleable.CircleFlowIndicator_circleSeparation, 2*radius+radius);
activeRadius = a.getDimension(R.styleable.CircleFlowIndicator_activeRadius, 0.5f);
// Retrieve the fade out time
fadeOutTime = a.getInt(R.styleable.CircleFlowIndicator_fadeOut, 0);
mCentered = a.getBoolean(R.styleable.CircleFlowIndicator_centered, false);
initColors(activeColor, inactiveColor, activeType, inactiveType);
}
private void initColors(int activeColor, int inactiveColor, int activeType,
int inactiveType) {
// Select the paint type given the type attr
switch (inactiveType) {
case STYLE_FILL:
mPaintInactive.setStyle(Style.FILL);
break;
default:
mPaintInactive.setStyle(Style.STROKE);
}
mPaintInactive.setColor(inactiveColor);
// Select the paint type given the type attr
switch (activeType) {
case STYLE_STROKE:
mPaintActive.setStyle(Style.STROKE);
break;
default:
mPaintActive.setStyle(Style.FILL);
}
mPaintActive.setColor(activeColor);
}
/*
* (non-Javadoc)
*
* @see android.view.View#onDraw(android.graphics.Canvas)
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int count = 3;
if (viewFlow != null) {
count = viewFlow.getViewsCount();
}
//this is the amount the first circle should be offset to make the entire thing centered
float centeringOffset = 0;
int leftPadding = getPaddingLeft();
// Draw stroked circles
for (int iLoop = 0; iLoop < count; iLoop++) {
canvas.drawCircle(leftPadding + radius
+ (iLoop * circleSeparation) + centeringOffset,
getPaddingTop() + radius, radius, mPaintInactive);
}
float cx = 0;
if (flowWidth != 0) {
// Draw the filled circle according to the current scroll
cx = (currentScroll * circleSeparation) / flowWidth;
}
// The flow width has been upadated yet. Draw the default position
canvas.drawCircle(leftPadding + radius + cx+centeringOffset, getPaddingTop()
+ radius, radius + activeRadius, mPaintActive);
}
/*
* (non-Javadoc)
*
* @see
* org.taptwo.android.widget.ViewFlow.ViewSwitchListener#onSwitched(android
* .view.View, int)
*/
@Override
public void onSwitched(View view, int position) {
}
/*
* (non-Javadoc)
*
* @see
* org.taptwo.android.widget.FlowIndicator#setViewFlow(org.taptwo.android
* .widget.ViewFlow)
*/
@Override
public void setViewFlow(ViewFlow view) {
resetTimer();
viewFlow = view;
flowWidth = viewFlow.getWidth();
invalidate();
}
/*
* (non-Javadoc)
*
* @see org.taptwo.android.widget.FlowIndicator#onScrolled(int, int, int,
* int)
*/
@Override
public void onScrolled(int h, int v, int oldh, int oldv) {
setVisibility(View.VISIBLE);
resetTimer();
flowWidth = viewFlow.getWidth();
if(viewFlow.getViewsCount()*flowWidth!=0){
currentScroll = h%(viewFlow.getViewsCount()*flowWidth);
}else {
currentScroll = h;
}
invalidate();
}
/*
* (non-Javadoc)
*
* @see android.view.View#onMeasure(int, int)
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
/**
* Determines the width of this view
*
* @param measureSpec
* A measureSpec packed into an int
* @return The width of the view, honoring constraints from measureSpec
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// We were told how big to be
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}
// Calculate the width according the views count
else {
int count = 3;
if (viewFlow != null) {
count = viewFlow.getViewsCount();
}
float temp = circleSeparation - 2*radius;
result = (int) (getPaddingLeft() + getPaddingRight()
+ (count * 2 * radius) + (count - 1) * temp + 1);
// Respect AT_MOST value if that was what is called for by
// measureSpec
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
/**
* Determines the height of this view
*
* @param measureSpec
* A measureSpec packed into an int
* @return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
// We were told how big to be
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}
// Measure the height
else {
result = (int) (2 * radius + getPaddingTop() + getPaddingBottom() + 1);
// Respect AT_MOST value if that was what is called for by
// measureSpec
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
/**
* Sets the fill color
*
* @param color
* ARGB value for the text
*/
public void setFillColor(int color) {
mPaintActive.setColor(color);
invalidate();
}
/**
* Sets the stroke color
*
* @param color
* ARGB value for the text
*/
public void setStrokeColor(int color) {
mPaintInactive.setColor(color);
invalidate();
}
/**
* Resets the fade out timer to 0. Creating a new one if needed
*/
private void resetTimer() {
// Only set the timer if we have a timeout of at least 1 millisecond
if (fadeOutTime > 0) {
// Check if we need to create a new timer
if (timer == null || timer._run == false) {
// Create and start a new timer
timer = new FadeTimer();
timer.execute();
} else {
// Reset the current tiemr to 0
timer.resetTimer();
}
}
}
/**
* Counts from 0 to the fade out time and animates the view away when
* reached
*/
private class FadeTimer extends AsyncTask<Void, Void, Void> {
// The current count
private int timer = 0;
// If we are inside the timing loop
private boolean _run = true;
public void resetTimer() {
timer = 0;
}
@Override
protected Void doInBackground(Void... arg0) {
while (_run) {
try {
// Wait for a millisecond
Thread.sleep(1);
// Increment the timer
timer++;
// Check if we've reached the fade out time
if (timer == fadeOutTime) {
// Stop running
_run = false;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
animation = AnimationUtils.loadAnimation(getContext(),
android.R.anim.fade_out);
animation.setAnimationListener(animationListener);
startAnimation(animation);
}
}
@Override
public void onAnimationEnd(Animation animation) {
setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
}
}
2.2创建,FlowInicator工具类
package com.example.test.util;
public interface FlowIndicator extends ViewFlow.ViewSwitchListener {
/**
* Set the current ViewFlow. This method is called by the ViewFlow when the
* FlowIndicator is attached to it.
*
* @param view
*/
public void setViewFlow(ViewFlow view);
/**
* The scroll position has been changed. A FlowIndicator may implement this
* method to reflect the current position
*
* @param h
* @param v
* @param oldh
* @param oldv
*/
public void onScrolled(int h, int v, int oldh, int oldv);
}
2.3创建ViewFlow工具类
/*
* Copyright (C) 2011 Patrik Åkerfeldt
*
* 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.
*/
package com.example.test.util;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AbsListView;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.Scroller;
import com.example.test.R;
import java.util.ArrayList;
import java.util.LinkedList;
/**
*
* A horizontally scrollable {@link ViewGroup} with items populated from an
* {@link Adapter}. The ViewFlow uses a buffer to store loaded {@link View}s in.
* The default size of the buffer is 3 elements on both sides of the currently
* visible {@link View}, making up a total buffer size of 3 * 2 + 1 = 7. The
* buffer size can be changed using the {@code sidebuffer} xml attribute.
*
* @author http://blog.csdn.net/finddreams
*/
public class ViewFlow extends AdapterView<Adapter> {
private static final int SNAP_VELOCITY = 1000;
private static final int INVALID_SCREEN = -1;
private final static int TOUCH_STATE_REST = 0;
private final static int TOUCH_STATE_SCROLLING = 1;
private LinkedList<View> mLoadedViews;
private int mCurrentBufferIndex;
private int mCurrentAdapterIndex;
private int mSideBuffer = 2;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mTouchState = TOUCH_STATE_REST;
private float mLastMotionX;
private int mTouchSlop;
private int mMaximumVelocity;
private int mCurrentScreen;
private int mNextScreen = INVALID_SCREEN;
private boolean mFirstLayout = true;
private ViewSwitchListener mViewSwitchListener;
private Adapter mAdapter;
private int mLastScrollDirection;
private AdapterDataSetObserver mDataSetObserver;
private FlowIndicator mIndicator;
private int mLastOrientation = -1;
private long timeSpan = 3000;
private Handler handler;
private OnGlobalLayoutListener orientationChangeListener = new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getViewTreeObserver().removeGlobalOnLayoutListener(
orientationChangeListener);
setSelection(mCurrentAdapterIndex);
}
};
/**
* Receives call backs when a new {@link View} has been scrolled to.
*/
public static interface ViewSwitchListener {
/**
* This method is called when a new View has been scrolled to.
*
* @param view
* the {@link View} currently in focus.
* @param position
* The position in the adapter of the {@link View} currently in focus.
*/
void onSwitched(View view, int position);
}
public ViewFlow(Context context) {
super(context);
mSideBuffer = 3;
init();
}
public ViewFlow(Context context, int sideBuffer) {
super(context);
mSideBuffer = sideBuffer;
init();
}
public ViewFlow(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray styledAttrs = context.obtainStyledAttributes(attrs,
R.styleable.ViewFlow);
mSideBuffer = styledAttrs.getInt(R.styleable.ViewFlow_sidebuffer, 3);
init();
}
private void init() {
mLoadedViews = new LinkedList<View>();
mScroller = new Scroller(getContext());
final ViewConfiguration configuration = ViewConfiguration
.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
public void startAutoFlowTimer(){
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
snapToScreen((mCurrentScreen+1)%getChildCount());
Message message = handler.obtainMessage(0);
sendMessageDelayed(message, timeSpan);
}
};
Message message = handler.obtainMessage(0);
handler.sendMessageDelayed(message, timeSpan);
}
public void stopAutoFlowTimer(){
if(handler!=null)
handler.removeMessages(0);
handler = null;
}
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
getViewTreeObserver().addOnGlobalLayoutListener(orientationChangeListener);
}
}
public int getViewsCount() {
return mSideBuffer;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY && !isInEditMode()) {
throw new IllegalStateException(
"ViewFlow can only be used in EXACTLY mode.");
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY && !isInEditMode()) {
throw new IllegalStateException(
"ViewFlow can only be used in EXACTLY mode.");
}
// The children are given the same width and height as the workspace
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
if (mFirstLayout) {
mScroller.startScroll(0, 0, mCurrentScreen * width, 0, 0);
mFirstLayout = false;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childLeft = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
final int childWidth = child.getMeasuredWidth();
child.layout(childLeft, 0, childLeft + childWidth,
child.getMeasuredHeight());
childLeft += childWidth;
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (getChildCount() == 0)
return false;
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
// Remember where the motion event started
mLastMotionX = x;
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
if(handler!=null)
handler.removeMessages(0);
break;
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(x - mLastMotionX);
boolean xMoved = xDiff > mTouchSlop;
if (xMoved) {
// Scroll if the user moved far enough along the X axis
mTouchState = TOUCH_STATE_SCROLLING;
}
if (mTouchState == TOUCH_STATE_SCROLLING) {
// Scroll to follow the motion event
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
final int scrollX = getScrollX();
if (deltaX < 0) {
if (scrollX > 0) {
scrollBy(Math.max(-scrollX, deltaX), 0);
}
} else if (deltaX > 0) {
final int availableToScroll = getChildAt(
getChildCount() - 1).getRight()
- scrollX - getWidth();
if (availableToScroll > 0) {
scrollBy(Math.min(availableToScroll, deltaX), 0);
}
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int velocityX = (int) velocityTracker.getXVelocity();
if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
// Fling hard enough to move left
snapToScreen(mCurrentScreen - 1);
} else if (velocityX < -SNAP_VELOCITY
&& mCurrentScreen < getChildCount() - 1) {
// Fling hard enough to move right
snapToScreen(mCurrentScreen + 1);
} else {
snapToDestination();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
mTouchState = TOUCH_STATE_REST;
if(handler!=null){
Message message = handler.obtainMessage(0);
handler.sendMessageDelayed(message, timeSpan);
}
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (getChildCount() == 0)
return false;
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
// Remember where the motion event started
mLastMotionX = x;
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
if(handler!=null)
handler.removeMessages(0);
break;
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(x - mLastMotionX);
boolean xMoved = xDiff > mTouchSlop;
if (xMoved) {
// Scroll if the user moved far enough along the X axis
mTouchState = TOUCH_STATE_SCROLLING;
}
if (mTouchState == TOUCH_STATE_SCROLLING) {
// Scroll to follow the motion event
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
final int scrollX = getScrollX();
if (deltaX < 0) {
if (scrollX > 0) {
scrollBy(Math.max(-scrollX, deltaX), 0);
}
} else if (deltaX > 0) {
final int availableToScroll = getChildAt(
getChildCount() - 1).getRight()
- scrollX - getWidth();
if (availableToScroll > 0) {
scrollBy(Math.min(availableToScroll, deltaX), 0);
}
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int velocityX = (int) velocityTracker.getXVelocity();
if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
// Fling hard enough to move left
snapToScreen(mCurrentScreen - 1);
} else if (velocityX < -SNAP_VELOCITY
&& mCurrentScreen < getChildCount() - 1) {
// Fling hard enough to move right
snapToScreen(mCurrentScreen + 1);
}
// else if (velocityX < -SNAP_VELOCITY
// && mCurrentScreen == getChildCount() - 1) {
// snapToScreen(0);
// }
// else if (velocityX > SNAP_VELOCITY
// && mCurrentScreen == 0) {
// snapToScreen(getChildCount() - 1);
// }
else {
snapToDestination();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
mTouchState = TOUCH_STATE_REST;
if(handler!=null){
Message message = handler.obtainMessage(0);
handler.sendMessageDelayed(message, timeSpan);
}
break;
case MotionEvent.ACTION_CANCEL:
snapToDestination();
mTouchState = TOUCH_STATE_REST;
}
return true;
}
@Override
protected void onScrollChanged(int h, int v, int oldh, int oldv) {
super.onScrollChanged(h, v, oldh, oldv);
if (mIndicator != null) {
/*
* The actual horizontal scroll origin does typically not match the
* perceived one. Therefore, we need to calculate the perceived
* horizontal scroll origin here, since we use a view buffer.
*/
int hPerceived = h + (mCurrentAdapterIndex - mCurrentBufferIndex)
* getWidth();
mIndicator.onScrolled(hPerceived, v, oldh, oldv);
}
}
private void snapToDestination() {
final int screenWidth = getWidth();
final int whichScreen = (getScrollX() + (screenWidth / 2))
/ screenWidth;
snapToScreen(whichScreen);
}
private void snapToScreen(int whichScreen) {
mLastScrollDirection = whichScreen - mCurrentScreen;
if (!mScroller.isFinished())
return;
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
mNextScreen = whichScreen;
final int newX = whichScreen * getWidth();
final int delta = newX - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
} else if (mNextScreen != INVALID_SCREEN) {
mCurrentScreen = Math.max(0,
Math.min(mNextScreen, getChildCount() - 1));
mNextScreen = INVALID_SCREEN;
postViewSwitched(mLastScrollDirection);
}
}
/**
* Scroll to the {@link View} in the view buffer specified by the index.
*
* @param indexInBuffer
* Index of the view in the view buffer.
*/
private void setVisibleView(int indexInBuffer, boolean uiThread) {
mCurrentScreen = Math.max(0,
Math.min(indexInBuffer, getChildCount() - 1));
int dx = (mCurrentScreen * getWidth()) - mScroller.getCurrX();
mScroller.startScroll(mScroller.getCurrX(), mScroller.getCurrY(), dx,
0, 0);
if(dx == 0)
onScrollChanged(mScroller.getCurrX() + dx, mScroller.getCurrY(), mScroller.getCurrX() + dx, mScroller.getCurrY());
if (uiThread)
invalidate();
else
postInvalidate();
}
/**
* Set the listener that will receive notifications every time the {code
* ViewFlow} scrolls.
*
* @param l
* the scroll listener
*/
public void setOnViewSwitchListener(ViewSwitchListener l) {
mViewSwitchListener = l;
}
@Override
public Adapter getAdapter() {
return mAdapter;
}
@Override
public void setAdapter(Adapter adapter) {
setAdapter(adapter, 0);
}
public void setAdapter(Adapter adapter, int initialPosition) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
mAdapter = adapter;
if (mAdapter != null) {
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
}
if (mAdapter == null || mAdapter.getCount() == 0)
return;
setSelection(initialPosition);
}
@Override
public View getSelectedView() {
return (mCurrentBufferIndex < mLoadedViews.size() ? mLoadedViews
.get(mCurrentBufferIndex) : null);
}
@Override
public int getSelectedItemPosition() {
return mCurrentAdapterIndex;
}
/**
* Set the FlowIndicator
*
* @param flowIndicator
*/
public void setFlowIndicator(FlowIndicator flowIndicator,int size) {
if(size==1){
return;
}else{
mIndicator = flowIndicator;
mIndicator.setViewFlow(this);
}
}
@Override
public void setSelection(int position) {
mNextScreen = INVALID_SCREEN;
mScroller.forceFinished(true);
if (mAdapter == null)
return;
position = Math.max(position, 0);
position = Math.min(position, mAdapter.getCount()-1);
ArrayList<View> recycleViews = new ArrayList<View>();
View recycleView;
while (!mLoadedViews.isEmpty()) {
recycleViews.add(recycleView = mLoadedViews.remove());
detachViewFromParent(recycleView);
}
View currentView = makeAndAddView(position, true,
(recycleViews.isEmpty() ? null : recycleViews.remove(0)));
mLoadedViews.addLast(currentView);
for(int offset = 1; mSideBuffer - offset >= 0; offset++) {
int leftIndex = position - offset;
int rightIndex = position + offset;
if(leftIndex >= 0)
mLoadedViews.addFirst(makeAndAddView(leftIndex, false,
(recycleViews.isEmpty() ? null : recycleViews.remove(0))));
if(rightIndex < mAdapter.getCount())
mLoadedViews.addLast(makeAndAddView(rightIndex, true,
(recycleViews.isEmpty() ? null : recycleViews.remove(0))));
}
mCurrentBufferIndex = mLoadedViews.indexOf(currentView);
mCurrentAdapterIndex = position;
for (View view : recycleViews) {
removeDetachedView(view, false);
}
requestLayout();
setVisibleView(mCurrentBufferIndex, false);
if (mIndicator != null) {
mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex),
mCurrentAdapterIndex);
}
if (mViewSwitchListener != null) {
mViewSwitchListener
.onSwitched(mLoadedViews.get(mCurrentBufferIndex),
mCurrentAdapterIndex);
}
}
private void resetFocus() {
mLoadedViews.clear();
removeAllViewsInLayout();
for (int i = Math.max(0, mCurrentAdapterIndex - mSideBuffer); i < Math
.min(mAdapter.getCount(), mCurrentAdapterIndex + mSideBuffer
+ 1); i++) {
mLoadedViews.addLast(makeAndAddView(i, true, null));
if (i == mCurrentAdapterIndex)
mCurrentBufferIndex = mLoadedViews.size() - 1;
}
requestLayout();
}
private void postViewSwitched(int direction) {
if (direction == 0)
return;
if (direction > 0) { // to the right
mCurrentAdapterIndex++;
mCurrentBufferIndex++;
// if(direction > 1) {
// mCurrentAdapterIndex += mAdapter.getCount() - 2;
// mCurrentBufferIndex += mAdapter.getCount() - 2;
// }
View recycleView = null;
// Remove view outside buffer range
if (mCurrentAdapterIndex > mSideBuffer) {
recycleView = mLoadedViews.removeFirst();
detachViewFromParent(recycleView);
// removeView(recycleView);
mCurrentBufferIndex--;
}
// Add new view to buffer
int newBufferIndex = mCurrentAdapterIndex + mSideBuffer;
if (newBufferIndex < mAdapter.getCount())
mLoadedViews.addLast(makeAndAddView(newBufferIndex, true,
recycleView));
} else { // to the left
mCurrentAdapterIndex--;
mCurrentBufferIndex--;
// if(direction < -1) {
// mCurrentAdapterIndex -= mAdapter.getCount() - 2;
// mCurrentBufferIndex -= mAdapter.getCount() - 2;
// }
View recycleView = null;
// Remove view outside buffer range
if (mAdapter.getCount() - 1 - mCurrentAdapterIndex > mSideBuffer) {
recycleView = mLoadedViews.removeLast();
detachViewFromParent(recycleView);
}
// Add new view to buffer
int newBufferIndex = mCurrentAdapterIndex - mSideBuffer;
if (newBufferIndex > -1) {
mLoadedViews.addFirst(makeAndAddView(newBufferIndex, false,
recycleView));
mCurrentBufferIndex++;
}
}
requestLayout();
setVisibleView(mCurrentBufferIndex, true);
if (mIndicator != null) {
mIndicator.onSwitched(mLoadedViews.get(mCurrentBufferIndex),
mCurrentAdapterIndex);
}
if (mViewSwitchListener != null) {
mViewSwitchListener
.onSwitched(mLoadedViews.get(mCurrentBufferIndex),
mCurrentAdapterIndex);
}
}
private View setupChild(View child, boolean addToEnd, boolean recycle) {
LayoutParams p = (LayoutParams) child
.getLayoutParams();
if (p == null) {
p = new AbsListView.LayoutParams(
LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT, 0);
}
if (recycle)
attachViewToParent(child, (addToEnd ? -1 : 0), p);
else
addViewInLayout(child, (addToEnd ? -1 : 0), p, true);
return child;
}
private View makeAndAddView(int position, boolean addToEnd, View convertView) {
View view = mAdapter.getView(position, convertView, this);
return setupChild(view, addToEnd, convertView != null);
}
class AdapterDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
View v = getChildAt(mCurrentBufferIndex);
if (v != null) {
for (int index = 0; index < mAdapter.getCount(); index++) {
if (v.equals(mAdapter.getItem(index))) {
mCurrentAdapterIndex = index;
break;
}
}
}
resetFocus();
}
@Override
public void onInvalidated() {
// Not yet implemented!
}
}
public void setTimeSpan(long timeSpan) {
this.timeSpan = timeSpan;
}
public void setmSideBuffer(int mSideBuffer) {
this.mSideBuffer = mSideBuffer;
}
}
2.4创建ProgressWebView工具类(点击广告要跳转的webview)
package com.example.test.util;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.webkit.WebView;
import android.widget.ProgressBar;
import com.example.test.R;
/**
* @Description: 带进度条的WebView
* @author http://blog.csdn.net/finddreams
*/
@SuppressWarnings("deprecation")
public class ProgressWebView extends WebView {
private ProgressBar progressbar;
public ProgressWebView(Context context, AttributeSet attrs) {
super(context, attrs);
progressbar = new ProgressBar(context, null,
android.R.attr.progressBarStyleHorizontal);
progressbar.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
10, 0, 0));
Drawable drawable = context.getResources().getDrawable(R.drawable.progress_bar_states);
progressbar.setProgressDrawable(drawable);
addView(progressbar);
// setWebViewClient(new WebViewClient(){});
setWebChromeClient(new WebChromeClient());
//是否支持缩放
getSettings().setSupportZoom(true);
getSettings().setBuiltInZoomControls(true);
}
public class WebChromeClient extends android.webkit.WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
progressbar.setVisibility(GONE);
} else {
if (progressbar.getVisibility() == GONE)
progressbar.setVisibility(VISIBLE);
progressbar.setProgress(newProgress);
}
super.onProgressChanged(view, newProgress);
}
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
LayoutParams lp = (LayoutParams) progressbar.getLayoutParams();
lp.x = l;
lp.y = t;
progressbar.setLayoutParams(lp);
super.onScrollChanged(l, t, oldl, oldt);
}
}
3.创建BaseWebActivity(及得在配置文件注册activity)
package com.example.test.activity;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.example.test.R;
import com.example.test.base.BaseApplication;
import com.example.test.util.ProgressWebView;
/**
* @Description:WebView界面,带自定义进度条显示
* @author http://blog.csdn.net/finddreams
*/
public class BaseWebActivity extends Activity {
private static String TAG = BaseWebActivity.class.getSimpleName() + "===";
protected ProgressWebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_baseweb);
BaseApplication.getApp().addActivity(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
// 有些情况下需要先清除透明flag
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
}
mWebView = (ProgressWebView) findViewById(R.id.baseweb_webview);
mWebView.getSettings().setJavaScriptEnabled(true);
initData();
}
protected void initData() {
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String urls = bundle.getString("url");
mWebView.loadUrl(urls);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setAppCacheEnabled(true);
//设置 缓存模式
mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
// 开启 DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// view.loadUrl(url);
return super.shouldOverrideUrlLoading(view, url);
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
try{
if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
mWebView.goBack();// 返回前一个页面
return true;
}else{
BaseApplication.getApp().finishActivity(this);
finish();
}
}catch (Exception e){
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mWebView = null;
BaseApplication.getApp().finishActivity(this);
}
}
3.drawable相关文件
3.1advertising_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp"></corners><!--设置圆角-->
</shape>
3.2progress_bar_states.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- 层叠 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@android:id/background">
<shape>
<corners android:radius="2dp" />
<gradient
android:angle="270"
android:centerColor="#E3E3E3"
android:endColor="#E6E6E6"
android:startColor="#C8C8C8" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="2dp" />
<gradient
android:centerColor="#4AEA2F"
android:endColor="#31CE15"
android:startColor="#5FEC46" />
</shape>
</clip>
</item>
</layer-list>
4.图片适配器ImagePagerAdapter
/*
* Copyright 2014 trinea.cn All right reserved. This software is the confidential and proprietary information of
* trinea.cn ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into with trinea.cn.
*/
package com.example.test.adapter;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.example.test.R;
import com.example.test.activity.BaseWebActivity;
import java.util.List;
/**
* @author http://blog.csdn.net/finddreams
* @Description: 图片适配器
*/
public class ImagePagerAdapter extends BaseAdapter {
private List<String> imageIdList;
private List<String> linkUrlArray;
private int size;
private boolean isInfiniteLoop;
private Dialog dialogs;
public ImagePagerAdapter(Context context, List<String> imageIdList,List<String> urllist, Dialog dialogs) {
this.imageIdList = imageIdList;
if (imageIdList != null) {
this.size = imageIdList.size();
}
this.linkUrlArray = urllist;
isInfiniteLoop = false;
this.dialogs = dialogs;
}
@Override
public int getCount() {
// Infinite loop
return isInfiniteLoop ? Integer.MAX_VALUE : imageIdList.size();
}
/**
* get really position
*
* @param position
* @return
*/
private int getPosition(int position) {
return isInfiniteLoop ? position % size : position;
}
@Override
public View getView(final int position, View view, ViewGroup container) {
final ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = holder.imageView = new ImageView(container.getContext());
holder.imageView
.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, ViewGroup.LayoutParams.MATCH_PARENT));
holder.imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
holder.imageView.setBackgroundResource(R.drawable.advertising_bg);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
//TODO:view的布局需要添加容器,glide对需要imageview不被设置tag
Context context = holder.imageView.getContext();
Glide.with(context)
.load(this.imageIdList.get(getPosition(position)))
.error(R.mipmap.loaderror).placeholder(R.mipmap.loaderror).
fallback(R.mipmap.loaderror).into(holder.imageView);
// Glide.with(context)
// .load(this.imageIdList.get(getPosition(position))).fitCenter().into(holder.imageView);
// imageLoader.displayImage(
// (String) this.imageIdList.get(getPosition(position)),
// holder.imageView, options);
holder.imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
String url = linkUrlArray.get(ImagePagerAdapter.this
.getPosition(position));
Bundle bundle = new Bundle();
bundle.putString("url", url);
Intent intent = new Intent(arg0.getContext(), BaseWebActivity.class);
intent.putExtras(bundle);
arg0.getContext().startActivity(intent);
dialogs.dismiss();
}
});
return view;
}
private static class ViewHolder {
ImageView imageView;
}
/**
* @return the isInfiniteLoop
*/
public boolean isInfiniteLoop() {
return isInfiniteLoop;
}
/**
* @param isInfiniteLoop the isInfiniteLoop to set
*/
public ImagePagerAdapter setInfiniteLoop(boolean isInfiniteLoop) {
this.isInfiniteLoop = isInfiniteLoop;
return this;
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
}
5.相关layout文件
5.1activity_main_advertising.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/rl_allguangao"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true">
<FrameLayout
android:id="@+id/framelayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.example.test.util.ViewFlow
android:id="@+id/viewflow"
android:layout_width="@dimen/px626"
android:layout_height="@dimen/px800" />
<com.example.test.util.CircleFlowIndicator
android:id="@+id/viewflowindic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="@dimen/px20"
android:layout_marginLeft="@dimen/px20"
android:layout_marginRight="@dimen/px20"
android:padding="2dip"
app:activeColor="#ffffff"
app:activeType="fill"
app:circleSeparation="20dip"
app:inactiveColor="#999999"
app:inactiveType="fill"
app:radius="4dip" />
</FrameLayout>
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/framelayout"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/px20"
android:scaleType="centerCrop"
android:src="@mipmap/advclose" />
</RelativeLayout>
</RelativeLayout>
5.2activity_baseweb.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.example.test.util.ProgressWebView
android:id="@+id/baseweb_webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fadeScrollbars="true"
android:scrollbarStyle="insideOverlay" />
</LinearLayout>
6.value相关文件
6.1arrt.xml
<attr name="rightPadding" format="dimension" />
<declare-styleable name="CircleImageView">
<attr name="border_width" format="dimension" />
<attr name="border_color" format="color" />
</declare-styleable>
<declare-styleable name="ViewFlow">
<attr name="sidebuffer" format="integer" />
</declare-styleable>
<declare-styleable name="CircleFlowIndicator">
<attr name="activeColor" format="color" />
<attr name="inactiveColor" format="color" />
<attr name="radius" format="dimension" />
<attr name="centered" format="boolean" />
<attr name="fadeOut" format="integer" />
<attr name="inactiveType">
<flag name="stroke" value="0" />
<flag name="fill" value="1" />
</attr>
<attr name="activeType">
<flag name="stroke" value="0" />
<flag name="fill" value="1" />
</attr>
<attr name="circleSeparation" format="dimension" />
<attr name="activeRadius" format="dimension" />
</declare-styleable>
6.2dimens.xml
<resources>
<dimen name="px20">8dp</dimen>
<dimen name="px626">300dp</dimen>
<dimen name="px800">400dp</dimen>
</resources>
6.3ids.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="tag_glide" />
</resources>
6.4设置完ids文件后再application的oncreate方法中添加下面代码
ViewTarget.setTagId(R.id.tag_glide);
7.主页Activity
/** * 广告相关 */ private ViewFlow mViewFlow; private CircleFlowIndicator mFlowIndicator; private ArrayList<String> imageUrlList = new ArrayList<String>(); private ArrayList<String> linkUrlArray = new ArrayList<String>(); private LayoutInflater inflater; private RelativeLayout layout; private ImageView iv_close; private Dialog dialogs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BaseApplication.getApp().addActivity(this);
initView();
initData();
}
/**
* 初始化控件
*/
private void initView() {
/**
* 广告相关
*/
inflater = LayoutInflater.from(MainActivity.this);
layout = (RelativeLayout) inflater.inflate(R.layout.activity_main_advertising, null);
iv_close = (ImageView) layout.findViewById(R.id.iv_close);
mViewFlow = (ViewFlow) layout.findViewById(R.id.viewflow);
mViewFlow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
dialogs.dismiss();
}
});
mFlowIndicator = (CircleFlowIndicator) layout.findViewById(R.id.viewflowindic);
iv_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialogs.dismiss();
}
});
/**
* 初始化数据
*/
private void initData() {
//广告相关
linkUrlArray.add("http://www.baidu.com");
linkUrlArray.add("https://www.csdn.net/");
linkUrlArray.add("https://www.oschina.net/");
imageUrlList.add("http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg");
imageUrlList.add("http://pic.people.com.cn/NMediaFile/2019/0610/MAIN201906101623000225736784754.jpg");
imageUrlList.add("http://www.gregscott.com/gjs_2007_spring/hummingbird/20070311_1948_100_0560.rufous_humminbird.jpg");
initBanner(imageUrlList);
}
private void showDialog() {
dialogs.setCanceledOnTouchOutside(false);//dialog弹出后会点击屏幕,dialog不消失;点击物理返回键dialog消失
dialogs.show();
dialogs.getWindow().setContentView(layout);
dialogs.getWindow().setBackgroundDrawableResource(R.color.transparent);
}
/**
* @param imageUrlList 广告图片url list
*/
private void initBanner(ArrayList<String> imageUrlList) {
dialogs = new AlertDialog.Builder(MainActivity.this).create();
mViewFlow.setAdapter(new ImagePagerAdapter(this, imageUrlList,
linkUrlArray, dialogs).setInfiniteLoop(true));
mViewFlow.setmSideBuffer(imageUrlList.size()); // 实际图片张数,
mViewFlow.setFlowIndicator(mFlowIndicator, imageUrlList.size());
mViewFlow.setTimeSpan(3500);
mViewFlow.setSelection(imageUrlList.size() * 1000); // 设置初始位置
if (imageUrlList.size() > 1) {//大于1页广告开启轮播
mViewFlow.startAutoFlowTimer(); // 启动自动播放
} else {
mFlowIndicator.setVisibility(View.GONE);
}
showDialog();
}
}