> transformers=new ArrayList<>();
41 | public void initData(){
42 | transformers.add(DefaultTransformer.class);
43 | transformers.add(AccordionTransformer.class);
44 | transformers.add(BackgroundToForegroundTransformer.class);
45 | transformers.add(ForegroundToBackgroundTransformer.class);
46 | transformers.add(CubeInTransformer.class);//兼容问题,慎用
47 | transformers.add(CubeOutTransformer.class);
48 | transformers.add(DepthPageTransformer.class);
49 | transformers.add(FlipHorizontalTransformer.class);
50 | transformers.add(FlipVerticalTransformer.class);
51 | transformers.add(RotateDownTransformer.class);
52 | transformers.add(RotateUpTransformer.class);
53 | transformers.add(ScaleInOutTransformer.class);
54 | transformers.add(StackTransformer.class);
55 | transformers.add(TabletTransformer.class);
56 | transformers.add(ZoomInTransformer.class);
57 | transformers.add(ZoomOutTranformer.class);
58 | transformers.add(ZoomOutSlideTransformer.class);
59 | }
60 |
61 | @Override
62 | protected void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | setContentView(R.layout.activity_banner_animation);
65 | initData();
66 | banner = (Banner) findViewById(R.id.banner);
67 | ListView listView = (ListView) findViewById(R.id.list);
68 | String[] data = getResources().getStringArray(R.array.anim);
69 | listView.setAdapter(new SampleAdapter(this, data));
70 | listView.setOnItemClickListener(this);
71 |
72 | banner.setImages(App.images)
73 | .setImageLoader(new GlideImageLoader())
74 | .setOnBannerListener(this)
75 | .start();
76 |
77 | }
78 |
79 | @Override
80 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
81 | banner.setBannerAnimation(transformers.get(position));
82 | }
83 |
84 | @Override
85 | public void OnBannerClick(int position) {
86 | Toast.makeText(getApplicationContext(),"你点击了:"+position,Toast.LENGTH_SHORT).show();
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/banner/ui/RoundAngleImageView.java:
--------------------------------------------------------------------------------
1 | package com.test.banner.ui;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Bitmap;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.Path;
11 | import android.graphics.PorterDuff;
12 | import android.graphics.PorterDuffXfermode;
13 | import android.graphics.RectF;
14 | import android.util.AttributeSet;
15 | import android.widget.ImageView;
16 |
17 |
18 | public class RoundAngleImageView extends ImageView {
19 |
20 | private Paint paint;
21 | private int roundWidth = 5;
22 | private int roundHeight = 5;
23 | private Paint paint2;
24 |
25 | public RoundAngleImageView(Context context, AttributeSet attrs, int defStyle) {
26 | super(context, attrs, defStyle);
27 | init(context, attrs);
28 | }
29 |
30 | public RoundAngleImageView(Context context, AttributeSet attrs) {
31 | super(context, attrs);
32 | init(context, attrs);
33 | }
34 |
35 | public RoundAngleImageView(Context context) {
36 | super(context);
37 | init(context, null);
38 | }
39 |
40 | private void init(Context context, AttributeSet attrs) {
41 | paint = new Paint();
42 | paint.setColor(Color.WHITE);
43 | paint.setAntiAlias(true);
44 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
45 | paint2 = new Paint();
46 | paint2.setXfermode(null);
47 | }
48 |
49 | @Override
50 | public void draw(Canvas canvas) {
51 | Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
52 | Canvas canvas2 = new Canvas(bitmap);
53 | super.draw(canvas2);
54 | drawLiftUp(canvas2);
55 | drawRightUp(canvas2);
56 | drawLiftDown(canvas2);
57 | drawRightDown(canvas2);
58 | canvas.drawBitmap(bitmap, 0, 0, paint2);
59 | bitmap.recycle();
60 | }
61 |
62 | private void drawLiftUp(Canvas canvas) {
63 | Path path = new Path();
64 | path.moveTo(0, roundHeight);
65 | path.lineTo(0, 0);
66 | path.lineTo(roundWidth, 0);
67 | path.arcTo(new RectF(
68 | 0,
69 | 0,
70 | roundWidth*2,
71 | roundHeight*2),
72 | -90,
73 | -90);
74 | path.close();
75 | canvas.drawPath(path, paint);
76 | }
77 |
78 | private void drawLiftDown(Canvas canvas) {
79 | Path path = new Path();
80 | path.moveTo(0, getHeight()-roundHeight);
81 | path.lineTo(0, getHeight());
82 | path.lineTo(roundWidth, getHeight());
83 | path.arcTo(new RectF(
84 | 0,
85 | getHeight()-roundHeight*2,
86 | 0+roundWidth*2,
87 | getHeight()),
88 | 90,
89 | 90);
90 | path.close();
91 | canvas.drawPath(path, paint);
92 | }
93 |
94 | private void drawRightDown(Canvas canvas) {
95 | Path path = new Path();
96 | path.moveTo(getWidth()-roundWidth, getHeight());
97 | path.lineTo(getWidth(), getHeight());
98 | path.lineTo(getWidth(), getHeight()-roundHeight);
99 | path.arcTo(new RectF(
100 | getWidth()-roundWidth*2,
101 | getHeight()-roundHeight*2,
102 | getWidth(),
103 | getHeight()), 0, 90);
104 | path.close();
105 | canvas.drawPath(path, paint);
106 | }
107 |
108 | private void drawRightUp(Canvas canvas) {
109 | Path path = new Path();
110 | path.moveTo(getWidth(), roundHeight);
111 | path.lineTo(getWidth(), 0);
112 | path.lineTo(getWidth()-roundWidth, 0);
113 | path.arcTo(new RectF(
114 | getWidth()-roundWidth*2,
115 | 0,
116 | getWidth(),
117 | 0+roundHeight*2),
118 | -90,
119 | 90);
120 | path.close();
121 | canvas.drawPath(path, paint);
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/app/src/main/java/com/test/banner/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.test.banner;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
7 | import androidx.appcompat.app.AppCompatActivity;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.AbsListView;
12 | import android.widget.AdapterView;
13 | import android.widget.ListView;
14 | import android.widget.Toast;
15 |
16 | import com.test.banner.demo.BannerAnimationActivity;
17 | import com.test.banner.demo.BannerLocalActivity;
18 | import com.test.banner.demo.BannerStyleActivity;
19 | import com.test.banner.demo.CustomBannerActivity;
20 | import com.test.banner.demo.CustomViewPagerActivity;
21 | import com.test.banner.demo.IndicatorPositionActivity;
22 | import com.test.banner.loader.GlideImageLoader;
23 | import com.youth.banner.Banner;
24 | import com.youth.banner.listener.OnBannerListener;
25 |
26 | import java.util.ArrayList;
27 | import java.util.Arrays;
28 | import java.util.List;
29 |
30 |
31 | public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener, OnBannerListener {
32 | static final int REFRESH_COMPLETE = 0X1112;
33 | SuperSwipeRefreshLayout mSwipeLayout;
34 | ListView listView;
35 | Banner banner;
36 |
37 | private Handler mHandler = new Handler() {
38 | public void handleMessage(android.os.Message msg) {
39 | switch (msg.what) {
40 | case REFRESH_COMPLETE:
41 | String[] urls = getResources().getStringArray(R.array.url4);
42 | List list = Arrays.asList(urls);
43 | List arrayList = new ArrayList(list);
44 | banner.update(arrayList);
45 | mSwipeLayout.setRefreshing(false);
46 | break;
47 | }
48 | }
49 | };
50 | @Override
51 | protected void onCreate(Bundle savedInstanceState) {
52 | super.onCreate(savedInstanceState);
53 | setContentView(R.layout.activity_main);
54 | mSwipeLayout = (SuperSwipeRefreshLayout) findViewById(R.id.swipe);
55 | mSwipeLayout.setOnRefreshListener(this);
56 | listView = (ListView) findViewById(R.id.list);
57 | View header = LayoutInflater.from(this).inflate(R.layout.header, null);
58 | banner = (Banner) header.findViewById(R.id.banner);
59 | banner.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, App.H / 4));
60 | listView.addHeaderView(banner);
61 |
62 | String[] data = getResources().getStringArray(R.array.demo_list);
63 | listView.setAdapter(new SampleAdapter(this,data));
64 | listView.setOnItemClickListener(this);
65 |
66 | //简单使用
67 | banner.setImages(App.images)
68 | .setImageLoader(new GlideImageLoader())
69 | .setOnBannerListener(this)
70 | .start();
71 |
72 | }
73 |
74 | @Override
75 | public void OnBannerClick(int position) {
76 | Toast.makeText(getApplicationContext(),"你点击了:"+position,Toast.LENGTH_SHORT).show();
77 | }
78 |
79 |
80 | //如果你需要考虑更好的体验,可以这么操作
81 | @Override
82 | protected void onStart() {
83 | super.onStart();
84 | //开始轮播
85 | banner.startAutoPlay();
86 | }
87 |
88 | @Override
89 | protected void onStop() {
90 | super.onStop();
91 | //结束轮播
92 | banner.stopAutoPlay();
93 | }
94 |
95 |
96 | @Override
97 | public void onRefresh() {
98 | mHandler.sendEmptyMessageDelayed(REFRESH_COMPLETE, 2000);
99 | }
100 |
101 | @Override
102 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
103 | switch (position){
104 | case 1:
105 | startActivity(new Intent(this, BannerAnimationActivity.class));
106 | break;
107 | case 2:
108 | startActivity(new Intent(this, BannerStyleActivity.class));
109 | break;
110 | case 3:
111 | startActivity(new Intent(this, IndicatorPositionActivity.class));
112 | break;
113 | case 4:
114 | startActivity(new Intent(this, CustomBannerActivity.class));
115 | break;
116 | case 5:
117 | startActivity(new Intent(this, BannerLocalActivity.class));
118 | break;
119 | case 6:
120 | startActivity(new Intent(this, CustomViewPagerActivity.class));
121 | break;
122 | }
123 | }
124 |
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/banner/src/main/java/com/youth/banner/transformer/ABaseTransformer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Toxic Bakery
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.youth.banner.transformer;
18 |
19 | import androidx.viewpager.widget.ViewPager.PageTransformer;
20 | import android.view.View;
21 |
22 | public abstract class ABaseTransformer implements PageTransformer {
23 |
24 | /**
25 | * Called each {@link #transformPage(View, float)}.
26 | *
27 | * @param page
28 | * Apply the transformation to this page
29 | * @param position
30 | * Position of page relative to the current front-and-center position of the pager. 0 is front and
31 | * center. 1 is one full page position to the right, and -1 is one page position to the left.
32 | */
33 | protected abstract void onTransform(View page, float position);
34 |
35 | /**
36 | * Apply a property transformation to the given page. For most use cases, this method should not be overridden.
37 | * Instead use {@link #transformPage(View, float)} to perform typical transformations.
38 | *
39 | * @param page
40 | * Apply the transformation to this page
41 | * @param position
42 | * Position of page relative to the current front-and-center position of the pager. 0 is front and
43 | * center. 1 is one full page position to the right, and -1 is one page position to the left.
44 | */
45 | @Override
46 | public void transformPage(View page, float position) {
47 | onPreTransform(page, position);
48 | onTransform(page, position);
49 | onPostTransform(page, position);
50 | }
51 |
52 | /**
53 | * If the position offset of a fragment is less than negative one or greater than one, returning true will set the
54 | * fragment alpha to 0f. Otherwise fragment alpha is always defaulted to 1f.
55 | *
56 | * @return
57 | */
58 | protected boolean hideOffscreenPages() {
59 | return true;
60 | }
61 |
62 | /**
63 | * Indicates if the default animations of the view pager should be used.
64 | *
65 | * @return
66 | */
67 | protected boolean isPagingEnabled() {
68 | return false;
69 | }
70 |
71 | /**
72 | * Called each {@link #transformPage(View, float)} before {{@link #onTransform(View, float)}.
73 | *
74 | * The default implementation attempts to reset all view properties. This is useful when toggling transforms that do
75 | * not modify the same page properties. For instance changing from a transformation that applies rotation to a
76 | * transformation that fades can inadvertently leave a fragment stuck with a rotation or with some degree of applied
77 | * alpha.
78 | *
79 | * @param page
80 | * Apply the transformation to this page
81 | * @param position
82 | * Position of page relative to the current front-and-center position of the pager. 0 is front and
83 | * center. 1 is one full page position to the right, and -1 is one page position to the left.
84 | */
85 | protected void onPreTransform(View page, float position) {
86 | final float width = page.getWidth();
87 |
88 | page.setRotationX(0);
89 | page.setRotationY(0);
90 | page.setRotation(0);
91 | page.setScaleX(1);
92 | page.setScaleY(1);
93 | page.setPivotX(0);
94 | page.setPivotY(0);
95 | page.setTranslationY(0);
96 | page.setTranslationX(isPagingEnabled() ? 0f : -width * position);
97 |
98 | if (hideOffscreenPages()) {
99 | page.setAlpha(position <= -1f || position >= 1f ? 0f : 1f);
100 | // page.setEnabled(false);
101 | } else {
102 | // page.setEnabled(true);
103 | page.setAlpha(1f);
104 | }
105 | }
106 |
107 | /**
108 | * Called each {@link #transformPage(View, float)} after {@link #onTransform(View, float)}.
109 | *
110 | * @param page
111 | * Apply the transformation to this page
112 | * @param position
113 | * Position of page relative to the current front-and-center position of the pager. 0 is front and
114 | * center. 1 is one full page position to the right, and -1 is one page position to the left.
115 | */
116 | protected void onPostTransform(View page, float position) {
117 | }
118 |
119 | /**
120 | * Same as {@link Math#min(double, double)} without double casting, zero closest to infinity handling, or NaN support.
121 | *
122 | * @param val
123 | * @param min
124 | * @return
125 | */
126 | protected static final float min(float val, float min) {
127 | return val < min ? min : val;
128 | }
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
174 | yes | $ANDROID_HOME/tools/bin/sdkmanager "build-tools;28.0.3"
--------------------------------------------------------------------------------
/banner/banner.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android图片轮播控件
2 | [](http://www.apache.org/licenses/LICENSE-2.0.html)
3 |
4 |
5 | ## 新框架发布,欢迎大家Star
6 |
7 | [XFrame - Android快速开发框架](https://github.com/youth5201314/XFrame)
8 |
9 | [XFrame详细功能文档预览](https://github.com/youth5201314/XFrame/wiki)
10 |
11 |
12 |
13 |
14 | 现在的绝大数app都有banner界面,实现循环播放多个广告图片和手动滑动循环等功能。因为ViewPager并不支持循环翻页,
15 | 所以要实现循环还得需要自己去动手,我就把项目中的控件剔了出来,希望大家觉得有用。目前框架可以进行不同样式、不同动画设置,
16 | 以及完善的api方法能满足大部分的需求了。
17 |
18 | ## 效果图
19 |
20 | |模式|图片
21 | |---|---|
22 | |指示器模式||
23 | |数字模式||
24 | |数字加标题模式||
25 | |指示器加标题模式
垂直显示||
26 | |指示器加标题模式
水平显示||
27 |
28 | ### 联系方式
29 | 
30 | * 如果有问题可以加群大家一起交流
31 | * 我的个人微博:https://weibo.com/u/3013494003 有兴趣的也可以关注,大家一起交流
32 |
33 | ## 常量
34 | |常量名称|描述|所属方法
35 | |---|---|---|
36 | |BannerConfig.NOT_INDICATOR| 不显示指示器和标题|setBannerStyle
37 | |BannerConfig.CIRCLE_INDICATOR| 显示圆形指示器|setBannerStyle
38 | |BannerConfig.NUM_INDICATOR| 显示数字指示器|setBannerStyle
39 | |BannerConfig.NUM_INDICATOR_TITLE| 显示数字指示器和标题|setBannerStyle
40 | |BannerConfig.CIRCLE_INDICATOR_TITLE| 显示圆形指示器和标题(垂直显示)|setBannerStyle
41 | |BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE| 显示圆形指示器和标题(水平显示)|setBannerStyle
42 | |BannerConfig.LEFT| 指示器居左|setIndicatorGravity
43 | |BannerConfig.CENTER| 指示器居中|setIndicatorGravity
44 | |BannerConfig.RIGHT| 指示器居右|setIndicatorGravity
45 |
46 | ## 动画常量类(setBannerAnimation方法调用)
47 | [ViewPagerTransforms](https://github.com/ToxicBakery/ViewPagerTransforms) `动画时集成的第三方库,可能有兼容问题导致position位置不准确,你可以选择参考动画然后自定义动画`
48 |
49 | |常量类名|
50 | |---|
51 | |Transformer.Default|
52 | |Transformer.Accordion|
53 | |Transformer.BackgroundToForeground|
54 | |Transformer.ForegroundToBackground|
55 | |Transformer.CubeIn|
56 | |Transformer.CubeOut|
57 | |Transformer.DepthPage|
58 | |Transformer.FlipHorizontal|
59 | |Transformer.FlipVertical|
60 | |Transformer.RotateDown|
61 | |Transformer.RotateUp|
62 | |Transformer.ScaleInOut|
63 | |Transformer.Stack|
64 | |Transformer.Tablet|
65 | |Transformer.ZoomIn|
66 | |Transformer.ZoomOut|
67 | |Transformer.ZoomOutSlide|
68 |
69 |
70 | ## 方法
71 | |方法名|描述|版本限制
72 | |---|---|---|
73 | |setBannerStyle(int bannerStyle)| 设置轮播样式(默认为CIRCLE_INDICATOR)|无
74 | |setIndicatorGravity(int type)| 设置指示器位置(没有标题默认为右边,有标题时默认左边)|无
75 | |isAutoPlay(boolean isAutoPlay)| 设置是否自动轮播(默认自动)|无
76 | |setViewPagerIsScroll(boolean isScroll)| 设置是否允许手动滑动轮播图(默认true)|1.4.5开始
77 | |update(List> imageUrls,List titles)| 更新图片和标题 |1.4.5开始
78 | |update(List> imageUrls)| 更新图片 |1.4.5开始
79 | |startAutoPlay()|开始轮播|1.4开始,此方法只作用于banner加载完毕-->需要在start()后执行
80 | |stopAutoPlay()|结束轮播|1.4开始,此方法只作用于banner加载完毕-->需要在start()后执行
81 | |start()|开始进行banner渲染(必须放到最后执行)|1.4开始
82 | |setOffscreenPageLimit(int limit)|同viewpager的方法作用一样|1.4.2开始
83 | |setBannerTitle(String[] titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.3.3结束
84 | |setBannerTitleList(List titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.3.3结束
85 | |setBannerTitles(List titles)| 设置轮播要显示的标题和图片对应(如果不传默认不显示标题)|1.4开始
86 | |setDelayTime(int time)| 设置轮播图片间隔时间(单位毫秒,默认为2000)|无
87 | |setImages(Object[]/List> imagesUrl)| 设置轮播图片(所有设置参数方法都放在此方法之前执行)|1.4后去掉数组传参
88 | |setImages(Object[]/List> imagesUrl,OnLoadImageListener listener)| 设置轮播图片,并且自定义图片加载方式|1.3.3结束
89 | |setOnBannerClickListener(this)|设置点击事件,下标是从1开始|无(1.4.9以后废弃了)
90 | |setOnBannerListener(this)|设置点击事件,下标是从0开始|1.4.9以后
91 | |setOnLoadImageListener(this)|设置图片加载事件,可以自定义图片加载方式|1.3.3结束
92 | |setImageLoader(Object implements ImageLoader)|设置图片加载器|1.4开始
93 | |setOnPageChangeListener(this)|设置viewpager的滑动监听|无
94 | |setBannerAnimation(Class extends PageTransformer> transformer)|设置viewpager的默认动画,传值见动画表|无
95 | |setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)|设置viewpager的自定义动画|无
96 |
97 | ## Attributes属性(banner布局文件中调用)
98 | |Attributes|forma|describe
99 | |---|---|---|
100 | |delay_time| integer|轮播间隔时间,默认2000
101 | |scroll_time| integer|轮播滑动执行时间,默认800
102 | |is_auto_play| boolean|是否自动轮播,默认true
103 | |title_background| color|reference|标题栏的背景色
104 | |title_textcolor| color|标题字体颜色
105 | |title_textsize| dimension|标题字体大小
106 | |title_height| dimension|标题栏高度
107 | |indicator_width| dimension|指示器圆形按钮的宽度
108 | |indicator_height| dimension|指示器圆形按钮的高度
109 | |indicator_margin| dimension|指示器之间的间距
110 | |indicator_drawable_selected| reference|指示器选中效果
111 | |indicator_drawable_unselected| reference|指示器未选中效果
112 | |image_scale_type| enum |和imageview的ScaleType作用一样
113 | |banner_default_image| reference | 当banner数据为空是显示的默认图片
114 | |banner_layout| reference |自定义banner布局文件,但是必须保证id的名称一样(你可以将banner的布局文件复制出来进行修改)
115 |
116 |
117 | ### [ 点击查看 ViewPager的PageTransformer用法 ]
118 |
119 |
120 | ## 使用步骤
121 |
122 | #### Step 1.依赖banner
123 | Gradle
124 | ```groovy
125 | dependencies{
126 | compile 'com.youth.banner:banner:1.4.10' //最新版本
127 | }
128 | ```
129 | 或者引用本地lib
130 | ```groovy
131 | compile project(':banner')
132 | ```
133 |
134 |
135 | #### Step 2.添加权限到你的 AndroidManifest.xml
136 | ```xml
137 |
138 |
139 |
140 |
141 |
142 | ```
143 |
144 | #### Step 3.在布局文件中添加Banner,可以设置自定义属性
145 | !!!此步骤可以省略,直接在Activity或者Fragment中new Banner();
146 | ```xml
147 |
152 | ```
153 |
154 | #### Step 4.重写图片加载器
155 | ```java
156 | public class GlideImageLoader extends ImageLoader {
157 | @Override
158 | public void displayImage(Context context, Object path, ImageView imageView) {
159 | /**
160 | 注意:
161 | 1.图片加载器由自己选择,这里不限制,只是提供几种使用方法
162 | 2.返回的图片路径为Object类型,由于不能确定你到底使用的那种图片加载器,
163 | 传输的到的是什么格式,那么这种就使用Object接收和返回,你只需要强转成你传输的类型就行,
164 | 切记不要胡乱强转!
165 | */
166 | eg:
167 |
168 | //Glide 加载图片简单用法
169 | Glide.with(context).load(path).into(imageView);
170 |
171 | //Picasso 加载图片简单用法
172 | Picasso.with(context).load(path).into(imageView);
173 |
174 | //用fresco加载图片简单用法,记得要写下面的createImageView方法
175 | Uri uri = Uri.parse((String) path);
176 | imageView.setImageURI(uri);
177 | }
178 |
179 | //提供createImageView 方法,如果不用可以不重写这个方法,主要是方便自定义ImageView的创建
180 | @Override
181 | public ImageView createImageView(Context context) {
182 | //使用fresco,需要创建它提供的ImageView,当然你也可以用自己自定义的具有图片加载功能的ImageView
183 | SimpleDraweeView simpleDraweeView=new SimpleDraweeView(context);
184 | return simpleDraweeView;
185 | }
186 | }
187 | ```
188 |
189 | #### Step 5.在Activity或者Fragment中配置Banner
190 |
191 | - 注意!start()方法必须放到最后执行,点击事件请放到start()前,每次都提交问题问为什么点击没有反应?需要轮播一圈才能点击?点击第一个怎么返回1?麻烦仔细阅读文档。
192 |
193 | ```java
194 | --------------------------简单使用-------------------------------
195 | @Override
196 | protected void onCreate(Bundle savedInstanceState) {
197 | super.onCreate(savedInstanceState);
198 | setContentView(R.layout.activity_main);
199 | Banner banner = (Banner) findViewById(R.id.banner);
200 | //设置图片加载器
201 | banner.setImageLoader(new GlideImageLoader());
202 | //设置图片集合
203 | banner.setImages(images);
204 | //banner设置方法全部调用完毕时最后调用
205 | banner.start();
206 | }
207 | --------------------------详细使用-------------------------------
208 | @Override
209 | protected void onCreate(Bundle savedInstanceState) {
210 | super.onCreate(savedInstanceState);
211 | setContentView(R.layout.activity_main);
212 | Banner banner = (Banner) findViewById(R.id.banner);
213 | //设置banner样式
214 | banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE);
215 | //设置图片加载器
216 | banner.setImageLoader(new GlideImageLoader());
217 | //设置图片集合
218 | banner.setImages(images);
219 | //设置banner动画效果
220 | banner.setBannerAnimation(Transformer.DepthPage);
221 | //设置标题集合(当banner样式有显示title时)
222 | banner.setBannerTitles(titles);
223 | //设置自动轮播,默认为true
224 | banner.isAutoPlay(true);
225 | //设置轮播时间
226 | banner.setDelayTime(1500);
227 | //设置指示器位置(当banner模式中有指示器时)
228 | banner.setIndicatorGravity(BannerConfig.CENTER);
229 | //banner设置方法全部调用完毕时最后调用
230 | banner.start();
231 | }
232 | -----------------当然如果你想偷下懒也可以这么用--------------------
233 | @Override
234 | protected void onCreate(Bundle savedInstanceState) {
235 | super.onCreate(savedInstanceState);
236 | setContentView(R.layout.activity_main);
237 | Banner banner = (Banner) findViewById(R.id.banner);
238 | banner.setImages(images).setImageLoader(new GlideImageLoader()).start();
239 | }
240 | ```
241 |
242 | #### Step 6.(可选)增加体验
243 | ```java
244 | //如果你需要考虑更好的体验,可以这么操作
245 | @Override
246 | protected void onStart() {
247 | super.onStart();
248 | //开始轮播
249 | banner.startAutoPlay();
250 | }
251 |
252 | @Override
253 | protected void onStop() {
254 | super.onStop();
255 | //结束轮播
256 | banner.stopAutoPlay();
257 | }
258 | ```
259 |
260 | ## 混淆代码
261 | ```java
262 | # glide 的混淆代码
263 | -keep public class * implements com.bumptech.glide.module.GlideModule
264 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
265 | **[] $VALUES;
266 | public *;
267 | }
268 | # banner 的混淆代码
269 | -keep class com.youth.banner.** {
270 | *;
271 | }
272 |
273 | ```
274 |
275 | [历史版本资源地址]
276 |
277 | [1.3.3以前旧版本使用文档地址]
278 |
279 | ## 常见问题
280 |
281 | * 问:eclipse怎么使用banner?
282 |
283 | * 答:`在历史版本列表中下载你想要版本的aar包提取最新资源/也可以自己把工程转成eclipse的`
284 | eclipse的集成demo群文件里有共享!
285 |
286 | * 问:怎么显示的一片空白?
287 | * 答:
288 | 1、没有添加网络权限
289 | 2、检查图片链接是否能打开。
290 | * 问:怎么加载其他图片资源(资源文件、文件、Uri、assets、raw、ContentProvider、sd卡资源)?
291 | * 答:列如!如果你使用的是glide,那么可以如下操作,其他图片图片加载框架可能有不同
292 | ```java
293 | //资源文件
294 | Integer[] images={R.mipmap.a,R.mipmap.b,R.mipmap.c};
295 | //Uri
296 | Uri uri = resourceIdToUri(context, R.mipmap.ic_launcher);
297 | Uri[] images={uri};
298 | //文件对象
299 | File[] images={"文件对象","文件对象"};
300 | //raw 两种方式
301 | String[] images={"Android.resource://com.frank.glide/raw/raw_1"};
302 | String[] images={"android.resource://com.frank.glide/raw/"+R.raw.raw_1"};
303 | //ContentProvider
304 | String[] images={"content://media/external/images/media/139469"};
305 | //assets
306 | String[] images={"file:///android_asset/f003.gif"};
307 | //sd卡资源
308 | String[] images={"file://"+ Environment.getExternalStorageDirectory().getPath()+"/test.jpg"};
309 |
310 | banner.setImages(images);//这里接收集合,上面写成集合太占地方,这个大家举一反三就行了啊
311 | ```
312 |
313 | * 问:设置banner指示器颜色怎么变成方的了?
314 |
315 | * 答:首先我先要说很多软件的指示器也是矩形的,然后banner的指示器可以设置color、资源图片、drawable文件夹自定义shape ,
316 | 所以形状你自己可以根据需求定义哦!
317 |
318 | * 问:为什么banner的点击事件没有反应,需要下一次轮播才行?点击第一个图片怎么返回1?
319 |
320 | * 答:请将点击事件放在start方法之前执行,start必须放到最后执行,详情可以看demo。
321 |
322 | ## Thanks
323 |
324 | - [ViewPagerTransforms](https://github.com/ToxicBakery/ViewPagerTransforms)
325 |
326 | ## 更新说明
327 |
328 | #### v1.4.10
329 | 很久没有维护banner了,有工作原因比较忙,也有经常遇见一些素质低的人,感觉整个世界都欠他们的,特别影响心情。就放弃更新维护了,
330 | 但是这半年每天邮箱都会收到各种建议反馈,也有很多人私信我,所以在此修复一些当前版本bug,
331 | 关于有朋友要求让轮播类型可以自定义,不局限于imageview的需求,
332 | 这个过段时间再发布一个全新的banner版本,会更加灵活,就不在原来的上面弄了,到时候分两个版本走!
333 |
334 | * 解决轮播手动滑动跳转问题:从第一张-->最后一张-->直接跳转到第二张
335 | * 解决update刷新轮播图崩溃问题
336 | * 将onPageScrolled和onPageSelected方法返回的position转成真实的position
337 | * 增加属性banner_default_image,设置当banner数据为空是显示的默认图片
338 | * 增加属性banner_layout,可以自定义布局文件,但是必须保证id的名称一样
339 | * 修改ViewPager偶发性的越界问题
340 | * SwipeRefreshLayout嵌套ViewPager的滑动冲突问题参考demo的SuperSwipeRefreshLayout类
341 |
342 | #### v1.4.9
343 | banner 优化更新
344 | * 废弃以前的点击事件(当然还是可以使用以前的方法),增加新的setOnBannerListener点击事件,下标从0开始
345 | * 解决update刷新轮播图后,会造成多次调用OnPageChangeListener的情况
346 | * 改变布局文件变量名,减少和工程冲突
347 |
348 | #### v1.4.8
349 | banner 优化更新
350 | * 修改点击事件返回下标偶尔越界问题
351 |
352 | #### v1.4.7
353 | banner 优化更新
354 | * 修复从第一个到最后一个,和从最后一个到第一个,数字和标题切换有点延迟的问题
355 |
356 | #### v1.4.6
357 | banner 优化更新
358 | * 修改demo,更容易理解
359 | * 修复第一张过渡第二张图片切换时间翻倍问题
360 | * 图片默认全屏展示
361 |
362 | #### v1.4.5
363 | banner 优化更新
364 | * 增加setViewPagerIsScroll(boolean isScroll)方法控制是否允许手动滑动轮播图,默认为true
365 | * 增加update()方法,方便更新图片
366 | * 解决最后一张图片切换到第一张,会出现卡顿(特别是不设置动画时有点明显)
367 |
368 | #### v1.4.3-1.4.4
369 | banner bug修改
370 | * 轮播图变少时刷新崩溃问题
371 | * 增加控制图片显示属性 image_scale_type 的属性值(center,center_crop,center_inside,fit_center,fit_end,fit_start,fit_xy,matrix),和 ImageView 的效果一样
372 | * 当只有一张图片时不显示圆形指示器和数字指示器
373 |
374 | #### v1.4.2
375 | banner优化更新<感谢 694551594,FeverCombo3,MIkeeJY >
376 | * !!!注意!!ImageLoader已从接口改变成抽象类,请调整下代码哦!
377 | * ImageLoader中增加ImageView控件创建方法createImageView(),可以满足fresco加载图片时扩展ImageView需求
378 | * 修改关于banner刷新时需要第二轮才会更新图片问题(同title更新图片不更新问题),具体看demo
379 | * 开放viewpager的setOffscreenPageLimit(int limit)方法
380 | * 优化banner在开始0s~20s之间会出现的内存泄漏问题
381 | * 优化最后一张到第一张之间滑动卡顿现象
382 |
383 | #### v1.4.1
384 | bug修改<感谢深圳-放飞,台北-Tom>
385 | * 第一次加载一张图片(不能滑动[正常])-->刷新-->第二次加载多张图片(不能滑动[bug])
386 | * 滑动事件传递拦截优化
387 | * demo里添加了下拉刷新和RecyclerView添加头部的两种方式
388 |
389 | #### v1.4
390 | 全新升级,此次更新比较大,如果不习惯使用1.4的还是可以用1.3.3
391 | * 去掉app:default_image="默认加载图片",需要可以自己在图片加载器中设置
392 | * 去掉glide图片加载相关代码全部用户自定义,外部通过实现(ImageLoader)去加载图片,尽力减少对第三方库的依赖
393 | * 去掉OnLoadImageListener图片加载监听事件,增加ImageLoader接口,通过setImageLoader设置图片加载器
394 | * 去掉isAutoPlay方法,改用startAutoPlay()|stopAutoPlay()这两个方法只能是渲染完(start())后调用
395 | * 调整代码结构和执行顺序,由start()进行最后渲染和逻辑判断,前面方法可以随意调用打乱顺序。
396 | * 将设置图片和标题的方法改成setImages和setBannerTitles,传参方式改成集合,如果要用数组可以Arrays.asList()转成集合使用
397 | * 调整默认样式为CIRCLE_INDICATOR
398 | * 禁止单张轮播手动滑动问题
399 | * banner的标题文字单位指定为TypedValue.COMPLEX_UNIT_PX
400 | * demo改版,如果需要1.3.3的demo请在QQ群中下载
401 |
402 |
403 | #### v1.3.3
404 | 优化轮播首尾过渡时间
405 | * 再实现轮播从最后一张到第一张时,在第一张前面加了一张图片用于过渡,保证轮播不太生硬。这样也就造成了第一张时间有点长,
406 | 开始没有发现,感谢大家的反馈,现在简单优化了下依然保留第一张500毫秒的时间用于过渡,让轮播保证流畅性。相信500毫秒的时间
407 | 对于效果没有什么影响,这段时间很忙后面会对算法进行修改,目前先这样用着吧。
408 |
409 | #### v1.3.2
410 | 修复bug
411 | * 解决在自动轮播中,轮播中途触摸图片/左右移动时停止轮播,抬起不自动轮播问题
412 |
413 | #### v1.3.1
414 | 修复bug
415 | * app:delay_time="轮播间隔时间" 参数无用问题
416 | * 在暂停轮播时,当你手动滑动时会重新开始轮播问题
417 | * 在轮播中,当你按住轮播时暂停,松开后不会轮播问题
418 |
419 | #### v1.2.9
420 | 修复bug以及更新功能
421 | * app:image_scale_type="fit_xy,和imageview的ScaleType作用一样,不过只提供了两个常用的"
422 | * 修复设置动画后点击事件失效的问题。
423 | * 取消setScrollerTime设置方法
424 |
425 | #### v1.2.8
426 | 增加ViewPager的切换速度设置方法,以及动画的重新封装
427 | * 整理了17种viewpager过渡动画,并整理为常量方便调用,改变了传参格式,如果不够用可以自行自定义动画
428 | * 增加setScrollerTime(int duration)设置viewpager的切换速度,(单位毫秒,默认800)
429 |
430 | #### v1.2.7
431 | 增加viewpager的切换默认几种动画,和自定义动画方法
432 | * setBannerAnimation(int type)设置viewpager的默认动画
433 | * setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)设置viewpager的自定义动画
434 |
435 | #### v1.2.5
436 | 修改bug
437 | * app:title_height="标题栏高度",高度过小文字不显示问题
438 |
439 | #### v1.2.4
440 | 优化更新
441 | * app:title_background="标题栏的背景色"
442 | * app:title_height="标题栏高度"
443 | * app:title_textcolor="标题字体颜色"
444 | * app:title_textsize="标题字体大小"
445 |
446 | #### v1.2.3
447 | 优化更新
448 | * 修复刷新banner从多张到1张时,还出现滑动的问题
449 | * demo增加功能
450 |
451 | #### v1.2.2
452 | 优化更新
453 | * 增加BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE显示圆形指示器和标题(水平显示)
454 | * 修改数字指示器时,变形问题。
455 |
456 | #### v1.2.1
457 | 优化更新
458 | * 修复NUM_INDICATOR和NUM_INDICATOR_TITLE模式下,没有轮播初始化为“1/1”的情况
459 | * 将图片加载默认图片取消,开发者可根据需要设置
460 |
461 | #### v1.2.0
462 | 优化更新
463 | * 修复小图片每次轮播被放大的问题
464 | * 开放了viewpager的滑动事件setOnPageChangeListener()
465 | * 增加触摸轮播图时暂停轮播,离开时继续轮播
466 | * 增加demo代码解释,关于刷新下标越界问题这个不存在,不懂的请看demo,
467 | 不要一出问题就认为是banner的错,看看自己的用法是不是出来问题。
468 |
469 | #### v1.1.9
470 | 优化更新
471 | * 当图片为一张时,禁止轮播
472 | * 优化标题初始化速度
473 |
474 | #### v1.1.8
475 | bug修改
476 | * 可能的存在的图片拉伸问题,替换为glide的图片加载大小计算
477 | * 修改关于非arraylist集合的强转问题。
478 |
479 | #### v1.1.7
480 | 应朋友的要求,做出更新
481 | * 为标题增加设置集合的方法:setBannerTitleList(List titles)
482 |
483 | #### v1.1.6
484 | 综合大家的反馈,做出一些更新
485 | * 将代码的常量全部提出到BannerConfig类里了,以前的代码,大家需要修改下
486 | * 有人喜欢xml属性的方式来设置参数,那么增加了几个xml属性
487 | app:default_image="默认加载图片"
488 | app:delay_time="轮播间隔时间"
489 | app:is_auto_play="是否自动轮播"
490 | * 增加了设置glide加载方式的默认加载图片方法
491 | app:default_image="默认加载图片"
492 | * 重新写了一下demo,方便大家更加容易懂
493 |
494 | #### v1.1.5
495 | 感谢朋友的反馈
496 | * 创建指示器初始化时默认的背景的添加,减少延迟等待更新
497 | * 优化指示器背景更新操作
498 |
499 | #### v1.1.4
500 | 更新内容
501 | * 增加setImages传参可以接收list集合
502 | * 优化在添加数据和创建指示器时的对象内存回收
503 |
504 | #### v1.1.3
505 | 修复了 <2316692710@qq.com> 朋友反馈的bug:
506 | * bug① 有标题的时候,向左滑动 ,会数组越界崩溃
507 | * bug② 指示器为数字的时候,向左滑动时会有一次显示为0/5
508 |
509 | #### v1.1.2
510 | 感谢 朋友提的意见,做出了如下更改:
511 | * 增加设置轮播图片,并且自定义图片加载方式:setImages(Object[] imagesUrl,OnLoadImageListener listener)
512 | * 增加设置图片加载事件,可以自定义图片加载方式:setOnBannerImageListener(this)
513 |
514 | #### v1.1.1
515 | 感谢 <969482412@qq.com> 朋友提的意见,做出了如下更改:
516 | * 增加圆形指示器的位置方法setIndicatorGravity(int type)
517 | * 增加设置是否自动轮播的方法isAutoPlay(boolean isAutoPlay)
518 |
519 | #### v1.1.0
520 | 感谢 <997058003@qq.com> 朋友提的意见,做出了如下更改:
521 | * 修改指示器样式
522 | * 增加5种轮播样式,更加灵活方便的运用轮播控件,满足项目需求
523 |
524 |
525 |
526 |
--------------------------------------------------------------------------------
/banner/src/main/java/com/youth/banner/WeakHandler.java:
--------------------------------------------------------------------------------
1 | package com.youth.banner;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 | import android.os.Message;
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 | import androidx.annotation.VisibleForTesting;
9 |
10 | import java.lang.ref.WeakReference;
11 | import java.util.concurrent.locks.Lock;
12 | import java.util.concurrent.locks.ReentrantLock;
13 |
14 | @SuppressWarnings("unused")
15 | public class WeakHandler {
16 | private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory
17 | private final ExecHandler mExec;
18 | private Lock mLock = new ReentrantLock();
19 | @SuppressWarnings("ConstantConditions")
20 | @VisibleForTesting
21 | final ChainedRef mRunnables = new ChainedRef(mLock, null);
22 |
23 | /**
24 | * Default constructor associates this handler with the {@link Looper} for the
25 | * current thread.
26 | *
27 | * If this thread does not have a looper, this handler won't be able to receive messages
28 | * so an exception is thrown.
29 | */
30 | public WeakHandler() {
31 | mCallback = null;
32 | mExec = new ExecHandler();
33 | }
34 |
35 | /**
36 | * Constructor associates this handler with the {@link Looper} for the
37 | * current thread and takes a callback interface in which you can handle
38 | * messages.
39 | *
40 | * If this thread does not have a looper, this handler won't be able to receive messages
41 | * so an exception is thrown.
42 | *
43 | * @param callback The callback interface in which to handle messages, or null.
44 | */
45 | public WeakHandler(@Nullable Handler.Callback callback) {
46 | mCallback = callback; // Hard referencing body
47 | mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler
48 | }
49 |
50 | /**
51 | * Use the provided {@link Looper} instead of the default one.
52 | *
53 | * @param looper The looper, must not be null.
54 | */
55 | public WeakHandler(@NonNull Looper looper) {
56 | mCallback = null;
57 | mExec = new ExecHandler(looper);
58 | }
59 |
60 | /**
61 | * Use the provided {@link Looper} instead of the default one and take a callback
62 | * interface in which to handle messages.
63 | *
64 | * @param looper The looper, must not be null.
65 | * @param callback The callback interface in which to handle messages, or null.
66 | */
67 | public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) {
68 | mCallback = callback;
69 | mExec = new ExecHandler(looper, new WeakReference<>(callback));
70 | }
71 |
72 | /**
73 | * Causes the Runnable r to be added to the message queue.
74 | * The runnable will be run on the thread to which this handler is
75 | * attached.
76 | *
77 | * @param r The Runnable that will be executed.
78 | *
79 | * @return Returns true if the Runnable was successfully placed in to the
80 | * message queue. Returns false on failure, usually because the
81 | * looper processing the message queue is exiting.
82 | */
83 | public final boolean post(@NonNull Runnable r) {
84 | return mExec.post(wrapRunnable(r));
85 | }
86 |
87 | /**
88 | * Causes the Runnable r to be added to the message queue, to be run
89 | * at a specific time given by uptimeMillis.
90 | * The time-base is {@link android.os.SystemClock#uptimeMillis}.
91 | * The runnable will be run on the thread to which this handler is attached.
92 | *
93 | * @param r The Runnable that will be executed.
94 | * @param uptimeMillis The absolute time at which the callback should run,
95 | * using the {@link android.os.SystemClock#uptimeMillis} time-base.
96 | *
97 | * @return Returns true if the Runnable was successfully placed in to the
98 | * message queue. Returns false on failure, usually because the
99 | * looper processing the message queue is exiting. Note that a
100 | * result of true does not mean the Runnable will be processed -- if
101 | * the looper is quit before the delivery time of the message
102 | * occurs then the message will be dropped.
103 | */
104 | public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
105 | return mExec.postAtTime(wrapRunnable(r), uptimeMillis);
106 | }
107 |
108 | /**
109 | * Causes the Runnable r to be added to the message queue, to be run
110 | * at a specific time given by uptimeMillis.
111 | * The time-base is {@link android.os.SystemClock#uptimeMillis}.
112 | * The runnable will be run on the thread to which this handler is attached.
113 | *
114 | * @param r The Runnable that will be executed.
115 | * @param uptimeMillis The absolute time at which the callback should run,
116 | * using the {@link android.os.SystemClock#uptimeMillis} time-base.
117 | *
118 | * @return Returns true if the Runnable was successfully placed in to the
119 | * message queue. Returns false on failure, usually because the
120 | * looper processing the message queue is exiting. Note that a
121 | * result of true does not mean the Runnable will be processed -- if
122 | * the looper is quit before the delivery time of the message
123 | * occurs then the message will be dropped.
124 | *
125 | * @see android.os.SystemClock#uptimeMillis
126 | */
127 | public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
128 | return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis);
129 | }
130 |
131 | /**
132 | * Causes the Runnable r to be added to the message queue, to be run
133 | * after the specified amount of time elapses.
134 | * The runnable will be run on the thread to which this handler
135 | * is attached.
136 | *
137 | * @param r The Runnable that will be executed.
138 | * @param delayMillis The delay (in milliseconds) until the Runnable
139 | * will be executed.
140 | *
141 | * @return Returns true if the Runnable was successfully placed in to the
142 | * message queue. Returns false on failure, usually because the
143 | * looper processing the message queue is exiting. Note that a
144 | * result of true does not mean the Runnable will be processed --
145 | * if the looper is quit before the delivery time of the message
146 | * occurs then the message will be dropped.
147 | */
148 | public final boolean postDelayed(Runnable r, long delayMillis) {
149 | return mExec.postDelayed(wrapRunnable(r), delayMillis);
150 | }
151 |
152 | /**
153 | * Posts a message to an object that implements Runnable.
154 | * Causes the Runnable r to executed on the next iteration through the
155 | * message queue. The runnable will be run on the thread to which this
156 | * handler is attached.
157 | * This method is only for use in very special circumstances -- it
158 | * can easily starve the message queue, cause ordering problems, or have
159 | * other unexpected side-effects.
160 | *
161 | * @param r The Runnable that will be executed.
162 | *
163 | * @return Returns true if the message was successfully placed in to the
164 | * message queue. Returns false on failure, usually because the
165 | * looper processing the message queue is exiting.
166 | */
167 | public final boolean postAtFrontOfQueue(Runnable r) {
168 | return mExec.postAtFrontOfQueue(wrapRunnable(r));
169 | }
170 |
171 | /**
172 | * Remove any pending posts of Runnable r that are in the message queue.
173 | */
174 | public final void removeCallbacks(Runnable r) {
175 | final WeakRunnable runnable = mRunnables.remove(r);
176 | if (runnable != null) {
177 | mExec.removeCallbacks(runnable);
178 | }
179 | }
180 |
181 | /**
182 | * Remove any pending posts of Runnable r with Object
183 | * token that are in the message queue. If token is null,
184 | * all callbacks will be removed.
185 | */
186 | public final void removeCallbacks(Runnable r, Object token) {
187 | final WeakRunnable runnable = mRunnables.remove(r);
188 | if (runnable != null) {
189 | mExec.removeCallbacks(runnable, token);
190 | }
191 | }
192 |
193 | /**
194 | * Pushes a message onto the end of the message queue after all pending messages
195 | * before the current time. It will be received in callback,
196 | * in the thread attached to this handler.
197 | *
198 | * @return Returns true if the message was successfully placed in to the
199 | * message queue. Returns false on failure, usually because the
200 | * looper processing the message queue is exiting.
201 | */
202 | public final boolean sendMessage(Message msg) {
203 | return mExec.sendMessage(msg);
204 | }
205 |
206 | /**
207 | * Sends a Message containing only the what value.
208 | *
209 | * @return Returns true if the message was successfully placed in to the
210 | * message queue. Returns false on failure, usually because the
211 | * looper processing the message queue is exiting.
212 | */
213 | public final boolean sendEmptyMessage(int what) {
214 | return mExec.sendEmptyMessage(what);
215 | }
216 |
217 | /**
218 | * Sends a Message containing only the what value, to be delivered
219 | * after the specified amount of time elapses.
220 | * @see #sendMessageDelayed(android.os.Message, long)
221 | *
222 | * @return Returns true if the message was successfully placed in to the
223 | * message queue. Returns false on failure, usually because the
224 | * looper processing the message queue is exiting.
225 | */
226 | public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
227 | return mExec.sendEmptyMessageDelayed(what, delayMillis);
228 | }
229 |
230 | /**
231 | * Sends a Message containing only the what value, to be delivered
232 | * at a specific time.
233 | * @see #sendMessageAtTime(android.os.Message, long)
234 | *
235 | * @return Returns true if the message was successfully placed in to the
236 | * message queue. Returns false on failure, usually because the
237 | * looper processing the message queue is exiting.
238 | */
239 | public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
240 | return mExec.sendEmptyMessageAtTime(what, uptimeMillis);
241 | }
242 |
243 | /**
244 | * Enqueue a message into the message queue after all pending messages
245 | * before (current time + delayMillis). You will receive it in
246 | * callback, in the thread attached to this handler.
247 | *
248 | * @return Returns true if the message was successfully placed in to the
249 | * message queue. Returns false on failure, usually because the
250 | * looper processing the message queue is exiting. Note that a
251 | * result of true does not mean the message will be processed -- if
252 | * the looper is quit before the delivery time of the message
253 | * occurs then the message will be dropped.
254 | */
255 | public final boolean sendMessageDelayed(Message msg, long delayMillis) {
256 | return mExec.sendMessageDelayed(msg, delayMillis);
257 | }
258 |
259 | /**
260 | * Enqueue a message into the message queue after all pending messages
261 | * before the absolute time (in milliseconds) uptimeMillis.
262 | * The time-base is {@link android.os.SystemClock#uptimeMillis}.
263 | * You will receive it in callback, in the thread attached
264 | * to this handler.
265 | *
266 | * @param uptimeMillis The absolute time at which the message should be
267 | * delivered, using the
268 | * {@link android.os.SystemClock#uptimeMillis} time-base.
269 | *
270 | * @return Returns true if the message was successfully placed in to the
271 | * message queue. Returns false on failure, usually because the
272 | * looper processing the message queue is exiting. Note that a
273 | * result of true does not mean the message will be processed -- if
274 | * the looper is quit before the delivery time of the message
275 | * occurs then the message will be dropped.
276 | */
277 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
278 | return mExec.sendMessageAtTime(msg, uptimeMillis);
279 | }
280 |
281 | /**
282 | * Enqueue a message at the front of the message queue, to be processed on
283 | * the next iteration of the message loop. You will receive it in
284 | * callback, in the thread attached to this handler.
285 | * This method is only for use in very special circumstances -- it
286 | * can easily starve the message queue, cause ordering problems, or have
287 | * other unexpected side-effects.
288 | *
289 | * @return Returns true if the message was successfully placed in to the
290 | * message queue. Returns false on failure, usually because the
291 | * looper processing the message queue is exiting.
292 | */
293 | public final boolean sendMessageAtFrontOfQueue(Message msg) {
294 | return mExec.sendMessageAtFrontOfQueue(msg);
295 | }
296 |
297 | /**
298 | * Remove any pending posts of messages with code 'what' that are in the
299 | * message queue.
300 | */
301 | public final void removeMessages(int what) {
302 | mExec.removeMessages(what);
303 | }
304 |
305 | /**
306 | * Remove any pending posts of messages with code 'what' and whose obj is
307 | * 'object' that are in the message queue. If object is null,
308 | * all messages will be removed.
309 | */
310 | public final void removeMessages(int what, Object object) {
311 | mExec.removeMessages(what, object);
312 | }
313 |
314 | /**
315 | * Remove any pending posts of callbacks and sent messages whose
316 | * obj is token. If token is null,
317 | * all callbacks and messages will be removed.
318 | */
319 | public final void removeCallbacksAndMessages(Object token) {
320 | mExec.removeCallbacksAndMessages(token);
321 | }
322 |
323 | /**
324 | * Check if there are any pending posts of messages with code 'what' in
325 | * the message queue.
326 | */
327 | public final boolean hasMessages(int what) {
328 | return mExec.hasMessages(what);
329 | }
330 |
331 | /**
332 | * Check if there are any pending posts of messages with code 'what' and
333 | * whose obj is 'object' in the message queue.
334 | */
335 | public final boolean hasMessages(int what, Object object) {
336 | return mExec.hasMessages(what, object);
337 | }
338 |
339 | public final Looper getLooper() {
340 | return mExec.getLooper();
341 | }
342 |
343 | private WeakRunnable wrapRunnable(@NonNull Runnable r) {
344 | //noinspection ConstantConditions
345 | if (r == null) {
346 | throw new NullPointerException("Runnable can't be null");
347 | }
348 | final ChainedRef hardRef = new ChainedRef(mLock, r);
349 | mRunnables.insertAfter(hardRef);
350 | return hardRef.wrapper;
351 | }
352 |
353 | private static class ExecHandler extends Handler {
354 | private final WeakReference mCallback;
355 |
356 | ExecHandler() {
357 | mCallback = null;
358 | }
359 |
360 | ExecHandler(WeakReference callback) {
361 | mCallback = callback;
362 | }
363 |
364 | ExecHandler(Looper looper) {
365 | super(looper);
366 | mCallback = null;
367 | }
368 |
369 | ExecHandler(Looper looper, WeakReference callback) {
370 | super(looper);
371 | mCallback = callback;
372 | }
373 |
374 | @Override
375 | public void handleMessage(@NonNull Message msg) {
376 | if (mCallback == null) {
377 | return;
378 | }
379 | final Handler.Callback callback = mCallback.get();
380 | if (callback == null) { // Already disposed
381 | return;
382 | }
383 | callback.handleMessage(msg);
384 | }
385 | }
386 |
387 | static class WeakRunnable implements Runnable {
388 | private final WeakReference mDelegate;
389 | private final WeakReference mReference;
390 |
391 | WeakRunnable(WeakReference delegate, WeakReference reference) {
392 | mDelegate = delegate;
393 | mReference = reference;
394 | }
395 |
396 | @Override
397 | public void run() {
398 | final Runnable delegate = mDelegate.get();
399 | final ChainedRef reference = mReference.get();
400 | if (reference != null) {
401 | reference.remove();
402 | }
403 | if (delegate != null) {
404 | delegate.run();
405 | }
406 | }
407 | }
408 |
409 | static class ChainedRef {
410 | @Nullable
411 | ChainedRef next;
412 | @Nullable
413 | ChainedRef prev;
414 | @NonNull
415 | final Runnable runnable;
416 | @NonNull
417 | final WeakRunnable wrapper;
418 |
419 | @NonNull
420 | Lock lock;
421 |
422 | public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) {
423 | this.runnable = r;
424 | this.lock = lock;
425 | this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this));
426 | }
427 |
428 | public WeakRunnable remove() {
429 | lock.lock();
430 | try {
431 | if (prev != null) {
432 | prev.next = next;
433 | }
434 | if (next != null) {
435 | next.prev = prev;
436 | }
437 | prev = null;
438 | next = null;
439 | } finally {
440 | lock.unlock();
441 | }
442 | return wrapper;
443 | }
444 |
445 | public void insertAfter(@NonNull ChainedRef candidate) {
446 | lock.lock();
447 | try {
448 | if (this.next != null) {
449 | this.next.prev = candidate;
450 | }
451 |
452 | candidate.next = this.next;
453 | this.next = candidate;
454 | candidate.prev = this;
455 | } finally {
456 | lock.unlock();
457 | }
458 | }
459 |
460 | @Nullable
461 | public WeakRunnable remove(Runnable obj) {
462 | lock.lock();
463 | try {
464 | ChainedRef curr = this.next; // Skipping head
465 | while (curr != null) {
466 | if (curr.runnable == obj) { // We do comparison exactly how Handler does inside
467 | return curr.remove();
468 | }
469 | curr = curr.next;
470 | }
471 | } finally {
472 | lock.unlock();
473 | }
474 | return null;
475 | }
476 | }
477 | }
--------------------------------------------------------------------------------
/banner/src/main/java/com/youth/banner/Banner.java:
--------------------------------------------------------------------------------
1 | package com.youth.banner;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import androidx.viewpager.widget.PagerAdapter;
6 | import androidx.viewpager.widget.ViewPager;
7 | import android.util.AttributeSet;
8 | import android.util.DisplayMetrics;
9 | import android.util.Log;
10 | import android.util.TypedValue;
11 | import android.view.Gravity;
12 | import android.view.LayoutInflater;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.widget.FrameLayout;
17 | import android.widget.ImageView;
18 | import android.widget.ImageView.ScaleType;
19 | import android.widget.LinearLayout;
20 | import android.widget.RelativeLayout;
21 | import android.widget.TextView;
22 |
23 | import com.youth.banner.listener.OnBannerClickListener;
24 | import com.youth.banner.listener.OnBannerListener;
25 | import com.youth.banner.loader.ImageLoaderInterface;
26 | import com.youth.banner.view.BannerViewPager;
27 |
28 | import java.lang.reflect.Field;
29 | import java.util.ArrayList;
30 | import java.util.List;
31 |
32 | import static androidx.viewpager.widget.ViewPager.OnPageChangeListener;
33 | import static androidx.viewpager.widget.ViewPager.PageTransformer;
34 |
35 | public class Banner extends FrameLayout implements OnPageChangeListener {
36 | public String tag = "banner";
37 | private int mIndicatorMargin = BannerConfig.PADDING_SIZE;
38 | private int mIndicatorWidth;
39 | private int mIndicatorHeight;
40 | private int indicatorSize;
41 | private int bannerBackgroundImage;
42 | private int bannerStyle = BannerConfig.CIRCLE_INDICATOR;
43 | private int delayTime = BannerConfig.TIME;
44 | private int scrollTime = BannerConfig.DURATION;
45 | private boolean isAutoPlay = BannerConfig.IS_AUTO_PLAY;
46 | private boolean isScroll = BannerConfig.IS_SCROLL;
47 | private int mIndicatorSelectedResId = R.drawable.gray_radius;
48 | private int mIndicatorUnselectedResId = R.drawable.white_radius;
49 | private int mLayoutResId = R.layout.banner;
50 | private int titleHeight;
51 | private int titleBackground;
52 | private int titleTextColor;
53 | private int titleTextSize;
54 | private int count = 0;
55 | private int currentItem;
56 | private int gravity = -1;
57 | private int lastPosition = 1;
58 | private int scaleType = 1;
59 | private List titles;
60 | private List imageUrls;
61 | private List imageViews;
62 | private List indicatorImages;
63 | private Context context;
64 | private BannerViewPager viewPager;
65 | private TextView bannerTitle, numIndicatorInside, numIndicator;
66 | private LinearLayout indicator, indicatorInside, titleView;
67 | private ImageView bannerDefaultImage;
68 | private ImageLoaderInterface imageLoader;
69 | private BannerPagerAdapter adapter;
70 | private OnPageChangeListener mOnPageChangeListener;
71 | private BannerScroller mScroller;
72 | private OnBannerClickListener bannerListener;
73 | private OnBannerListener listener;
74 | private DisplayMetrics dm;
75 |
76 | private WeakHandler handler = new WeakHandler();
77 |
78 | public Banner(Context context) {
79 | this(context, null);
80 | }
81 |
82 | public Banner(Context context, AttributeSet attrs) {
83 | this(context, attrs, 0);
84 | }
85 |
86 | public Banner(Context context, AttributeSet attrs, int defStyle) {
87 | super(context, attrs, defStyle);
88 | this.context = context;
89 | titles = new ArrayList<>();
90 | imageUrls = new ArrayList<>();
91 | imageViews = new ArrayList<>();
92 | indicatorImages = new ArrayList<>();
93 | dm = context.getResources().getDisplayMetrics();
94 | indicatorSize = dm.widthPixels / 80;
95 | initView(context, attrs);
96 | }
97 |
98 | private void initView(Context context, AttributeSet attrs) {
99 | imageViews.clear();
100 | handleTypedArray(context, attrs);
101 | View view = LayoutInflater.from(context).inflate(mLayoutResId, this, true);
102 | bannerDefaultImage = (ImageView) view.findViewById(R.id.bannerDefaultImage);
103 | viewPager = (BannerViewPager) view.findViewById(R.id.bannerViewPager);
104 | titleView = (LinearLayout) view.findViewById(R.id.titleView);
105 | indicator = (LinearLayout) view.findViewById(R.id.circleIndicator);
106 | indicatorInside = (LinearLayout) view.findViewById(R.id.indicatorInside);
107 | bannerTitle = (TextView) view.findViewById(R.id.bannerTitle);
108 | numIndicator = (TextView) view.findViewById(R.id.numIndicator);
109 | numIndicatorInside = (TextView) view.findViewById(R.id.numIndicatorInside);
110 | bannerDefaultImage.setImageResource(bannerBackgroundImage);
111 | initViewPagerScroll();
112 | }
113 |
114 | private void handleTypedArray(Context context, AttributeSet attrs) {
115 | if (attrs == null) {
116 | return;
117 | }
118 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Banner);
119 | mIndicatorWidth = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_width, indicatorSize);
120 | mIndicatorHeight = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_height, indicatorSize);
121 | mIndicatorMargin = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_margin, BannerConfig.PADDING_SIZE);
122 | mIndicatorSelectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_selected, R.drawable.gray_radius);
123 | mIndicatorUnselectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_unselected, R.drawable.white_radius);
124 | scaleType = typedArray.getInt(R.styleable.Banner_image_scale_type, scaleType);
125 | delayTime = typedArray.getInt(R.styleable.Banner_delay_time, BannerConfig.TIME);
126 | scrollTime = typedArray.getInt(R.styleable.Banner_scroll_time, BannerConfig.DURATION);
127 | isAutoPlay = typedArray.getBoolean(R.styleable.Banner_is_auto_play, BannerConfig.IS_AUTO_PLAY);
128 | titleBackground = typedArray.getColor(R.styleable.Banner_title_background, BannerConfig.TITLE_BACKGROUND);
129 | titleHeight = typedArray.getDimensionPixelSize(R.styleable.Banner_title_height, BannerConfig.TITLE_HEIGHT);
130 | titleTextColor = typedArray.getColor(R.styleable.Banner_title_textcolor, BannerConfig.TITLE_TEXT_COLOR);
131 | titleTextSize = typedArray.getDimensionPixelSize(R.styleable.Banner_title_textsize, BannerConfig.TITLE_TEXT_SIZE);
132 | mLayoutResId = typedArray.getResourceId(R.styleable.Banner_banner_layout, mLayoutResId);
133 | bannerBackgroundImage = typedArray.getResourceId(R.styleable.Banner_banner_default_image, R.drawable.no_banner);
134 | typedArray.recycle();
135 | }
136 |
137 | private void initViewPagerScroll() {
138 | try {
139 | Field mField = ViewPager.class.getDeclaredField("mScroller");
140 | mField.setAccessible(true);
141 | mScroller = new BannerScroller(viewPager.getContext());
142 | mScroller.setDuration(scrollTime);
143 | mField.set(viewPager, mScroller);
144 | } catch (Exception e) {
145 | Log.e(tag, e.getMessage());
146 | }
147 | }
148 |
149 |
150 | public Banner isAutoPlay(boolean isAutoPlay) {
151 | this.isAutoPlay = isAutoPlay;
152 | return this;
153 | }
154 |
155 | public Banner setImageLoader(ImageLoaderInterface imageLoader) {
156 | this.imageLoader = imageLoader;
157 | return this;
158 | }
159 |
160 | public Banner setDelayTime(int delayTime) {
161 | this.delayTime = delayTime;
162 | return this;
163 | }
164 |
165 | public Banner setIndicatorGravity(int type) {
166 | switch (type) {
167 | case BannerConfig.LEFT:
168 | this.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
169 | break;
170 | case BannerConfig.CENTER:
171 | this.gravity = Gravity.CENTER;
172 | break;
173 | case BannerConfig.RIGHT:
174 | this.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
175 | break;
176 | }
177 | return this;
178 | }
179 |
180 | public Banner setBannerAnimation(Class extends PageTransformer> transformer) {
181 | try {
182 | setPageTransformer(true, transformer.newInstance());
183 | } catch (Exception e) {
184 | Log.e(tag, "Please set the PageTransformer class");
185 | }
186 | return this;
187 | }
188 |
189 | /**
190 | * Set the number of pages that should be retained to either side of the
191 | * current page in the view hierarchy in an idle state. Pages beyond this
192 | * limit will be recreated from the adapter when needed.
193 | *
194 | * @param limit How many pages will be kept offscreen in an idle state.
195 | * @return Banner
196 | */
197 | public Banner setOffscreenPageLimit(int limit) {
198 | if (viewPager != null) {
199 | viewPager.setOffscreenPageLimit(limit);
200 | }
201 | return this;
202 | }
203 |
204 | /**
205 | * Set a {@link PageTransformer} that will be called for each attached page whenever
206 | * the scroll position is changed. This allows the application to apply custom property
207 | * transformations to each page, overriding the default sliding look and feel.
208 | *
209 | * @param reverseDrawingOrder true if the supplied PageTransformer requires page views
210 | * to be drawn from last to first instead of first to last.
211 | * @param transformer PageTransformer that will modify each page's animation properties
212 | * @return Banner
213 | */
214 | public Banner setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
215 | viewPager.setPageTransformer(reverseDrawingOrder, transformer);
216 | return this;
217 | }
218 |
219 | public Banner setBannerTitles(List titles) {
220 | this.titles = titles;
221 | return this;
222 | }
223 |
224 | public Banner setBannerStyle(int bannerStyle) {
225 | this.bannerStyle = bannerStyle;
226 | return this;
227 | }
228 |
229 | public Banner setViewPagerIsScroll(boolean isScroll) {
230 | this.isScroll = isScroll;
231 | return this;
232 | }
233 |
234 | public Banner setImages(List> imageUrls) {
235 | this.imageUrls = imageUrls;
236 | this.count = imageUrls.size();
237 | return this;
238 | }
239 |
240 | public void update(List> imageUrls, List titles) {
241 | this.titles.clear();
242 | this.titles.addAll(titles);
243 | update(imageUrls);
244 | }
245 |
246 | public void update(List> imageUrls) {
247 | this.imageUrls.clear();
248 | this.imageViews.clear();
249 | this.indicatorImages.clear();
250 | this.imageUrls.addAll(imageUrls);
251 | this.count = this.imageUrls.size();
252 | start();
253 | }
254 |
255 | public void updateBannerStyle(int bannerStyle) {
256 | indicator.setVisibility(GONE);
257 | numIndicator.setVisibility(GONE);
258 | numIndicatorInside.setVisibility(GONE);
259 | indicatorInside.setVisibility(GONE);
260 | bannerTitle.setVisibility(View.GONE);
261 | titleView.setVisibility(View.GONE);
262 | this.bannerStyle = bannerStyle;
263 | start();
264 | }
265 |
266 | public Banner start() {
267 | setBannerStyleUI();
268 | setImageList(imageUrls);
269 | setData();
270 | return this;
271 | }
272 |
273 | private void setTitleStyleUI() {
274 | if (titles.size() != imageUrls.size()) {
275 | throw new RuntimeException("[Banner] --> The number of titles and images is different");
276 | }
277 | if (titleBackground != -1) {
278 | titleView.setBackgroundColor(titleBackground);
279 | }
280 | if (titleHeight != -1) {
281 | titleView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, titleHeight));
282 | }
283 | if (titleTextColor != -1) {
284 | bannerTitle.setTextColor(titleTextColor);
285 | }
286 | if (titleTextSize != -1) {
287 | bannerTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextSize);
288 | }
289 | if (titles != null && titles.size() > 0) {
290 | bannerTitle.setText(titles.get(0));
291 | bannerTitle.setVisibility(View.VISIBLE);
292 | titleView.setVisibility(View.VISIBLE);
293 | }
294 | }
295 |
296 | private void setBannerStyleUI() {
297 | int visibility =count > 1 ? View.VISIBLE :View.GONE;
298 | switch (bannerStyle) {
299 | case BannerConfig.CIRCLE_INDICATOR:
300 | indicator.setVisibility(visibility);
301 | break;
302 | case BannerConfig.NUM_INDICATOR:
303 | numIndicator.setVisibility(visibility);
304 | break;
305 | case BannerConfig.NUM_INDICATOR_TITLE:
306 | numIndicatorInside.setVisibility(visibility);
307 | setTitleStyleUI();
308 | break;
309 | case BannerConfig.CIRCLE_INDICATOR_TITLE:
310 | indicator.setVisibility(visibility);
311 | setTitleStyleUI();
312 | break;
313 | case BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE:
314 | indicatorInside.setVisibility(visibility);
315 | setTitleStyleUI();
316 | break;
317 | }
318 | }
319 |
320 | private void initImages() {
321 | imageViews.clear();
322 | if (bannerStyle == BannerConfig.CIRCLE_INDICATOR ||
323 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE ||
324 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE) {
325 | createIndicator();
326 | } else if (bannerStyle == BannerConfig.NUM_INDICATOR_TITLE) {
327 | numIndicatorInside.setText("1/" + count);
328 | } else if (bannerStyle == BannerConfig.NUM_INDICATOR) {
329 | numIndicator.setText("1/" + count);
330 | }
331 | }
332 |
333 | private void setImageList(List> imagesUrl) {
334 | if (imagesUrl == null || imagesUrl.size() <= 0) {
335 | bannerDefaultImage.setVisibility(VISIBLE);
336 | Log.e(tag, "The image data set is empty.");
337 | return;
338 | }
339 | bannerDefaultImage.setVisibility(GONE);
340 | initImages();
341 | for (int i = 0; i <= count + 1; i++) {
342 | View imageView = null;
343 | if (imageLoader != null) {
344 | imageView = imageLoader.createImageView(context);
345 | }
346 | if (imageView == null) {
347 | imageView = new ImageView(context);
348 | }
349 | setScaleType(imageView);
350 | Object url = null;
351 | if (i == 0) {
352 | url = imagesUrl.get(count - 1);
353 | } else if (i == count + 1) {
354 | url = imagesUrl.get(0);
355 | } else {
356 | url = imagesUrl.get(i - 1);
357 | }
358 | imageViews.add(imageView);
359 | if (imageLoader != null)
360 | imageLoader.displayImage(context, url, imageView);
361 | else
362 | Log.e(tag, "Please set images loader.");
363 | }
364 | }
365 |
366 | private void setScaleType(View imageView) {
367 | if (imageView instanceof ImageView) {
368 | ImageView view = ((ImageView) imageView);
369 | switch (scaleType) {
370 | case 0:
371 | view.setScaleType(ScaleType.CENTER);
372 | break;
373 | case 1:
374 | view.setScaleType(ScaleType.CENTER_CROP);
375 | break;
376 | case 2:
377 | view.setScaleType(ScaleType.CENTER_INSIDE);
378 | break;
379 | case 3:
380 | view.setScaleType(ScaleType.FIT_CENTER);
381 | break;
382 | case 4:
383 | view.setScaleType(ScaleType.FIT_END);
384 | break;
385 | case 5:
386 | view.setScaleType(ScaleType.FIT_START);
387 | break;
388 | case 6:
389 | view.setScaleType(ScaleType.FIT_XY);
390 | break;
391 | case 7:
392 | view.setScaleType(ScaleType.MATRIX);
393 | break;
394 | }
395 |
396 | }
397 | }
398 |
399 | private void createIndicator() {
400 | indicatorImages.clear();
401 | indicator.removeAllViews();
402 | indicatorInside.removeAllViews();
403 | for (int i = 0; i < count; i++) {
404 | ImageView imageView = new ImageView(context);
405 | imageView.setScaleType(ScaleType.CENTER_CROP);
406 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIndicatorWidth, mIndicatorHeight);
407 | params.leftMargin = mIndicatorMargin;
408 | params.rightMargin = mIndicatorMargin;
409 | if (i == 0) {
410 | imageView.setImageResource(mIndicatorSelectedResId);
411 | } else {
412 | imageView.setImageResource(mIndicatorUnselectedResId);
413 | }
414 | indicatorImages.add(imageView);
415 | if (bannerStyle == BannerConfig.CIRCLE_INDICATOR ||
416 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE)
417 | indicator.addView(imageView, params);
418 | else if (bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE)
419 | indicatorInside.addView(imageView, params);
420 | }
421 | }
422 |
423 |
424 | private void setData() {
425 | currentItem = 1;
426 | if (adapter == null) {
427 | adapter = new BannerPagerAdapter();
428 | viewPager.addOnPageChangeListener(this);
429 | }
430 | viewPager.setAdapter(adapter);
431 | viewPager.setFocusable(true);
432 | viewPager.setCurrentItem(1);
433 | if (gravity != -1)
434 | indicator.setGravity(gravity);
435 | if (isScroll && count > 1) {
436 | viewPager.setScrollable(true);
437 | } else {
438 | viewPager.setScrollable(false);
439 | }
440 | if (isAutoPlay)
441 | startAutoPlay();
442 | }
443 |
444 |
445 | public void startAutoPlay() {
446 | handler.removeCallbacks(task);
447 | handler.postDelayed(task, delayTime);
448 | }
449 |
450 | public void stopAutoPlay() {
451 | handler.removeCallbacks(task);
452 | }
453 |
454 | private final Runnable task = new Runnable() {
455 | @Override
456 | public void run() {
457 | if (count > 1 && isAutoPlay) {
458 | currentItem = currentItem % (count + 1) + 1;
459 | // Log.i(tag, "curr:" + currentItem + " count:" + count);
460 | if (currentItem == 1) {
461 | viewPager.setCurrentItem(currentItem, false);
462 | handler.post(task);
463 | } else {
464 | viewPager.setCurrentItem(currentItem);
465 | handler.postDelayed(task, delayTime);
466 | }
467 | }
468 | }
469 | };
470 |
471 | @Override
472 | public boolean dispatchTouchEvent(MotionEvent ev) {
473 | // Log.i(tag, ev.getAction() + "--" + isAutoPlay);
474 | if (isAutoPlay) {
475 | int action = ev.getAction();
476 | if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
477 | || action == MotionEvent.ACTION_OUTSIDE) {
478 | startAutoPlay();
479 | } else if (action == MotionEvent.ACTION_DOWN) {
480 | stopAutoPlay();
481 | }
482 | }
483 | return super.dispatchTouchEvent(ev);
484 | }
485 |
486 | /**
487 | * 返回真实的位置
488 | *
489 | * @param position
490 | * @return 下标从0开始
491 | */
492 | public int toRealPosition(int position) {
493 | int realPosition = (position - 1) % count;
494 | if (realPosition < 0)
495 | realPosition += count;
496 | return realPosition;
497 | }
498 |
499 | class BannerPagerAdapter extends PagerAdapter {
500 |
501 | @Override
502 | public int getCount() {
503 | return imageViews.size();
504 | }
505 |
506 | @Override
507 | public boolean isViewFromObject(View view, Object object) {
508 | return view == object;
509 | }
510 |
511 | @Override
512 | public Object instantiateItem(ViewGroup container, final int position) {
513 | container.addView(imageViews.get(position));
514 | View view = imageViews.get(position);
515 | if (bannerListener != null) {
516 | view.setOnClickListener(new OnClickListener() {
517 | @Override
518 | public void onClick(View v) {
519 | Log.e(tag, "你正在使用旧版点击事件接口,下标是从1开始," +
520 | "为了体验请更换为setOnBannerListener,下标从0开始计算");
521 | bannerListener.OnBannerClick(position);
522 | }
523 | });
524 | }
525 | if (listener != null) {
526 | view.setOnClickListener(new OnClickListener() {
527 | @Override
528 | public void onClick(View v) {
529 | listener.OnBannerClick(toRealPosition(position));
530 | }
531 | });
532 | }
533 | return view;
534 | }
535 |
536 | @Override
537 | public void destroyItem(ViewGroup container, int position, Object object) {
538 | container.removeView((View) object);
539 | }
540 |
541 | }
542 |
543 | @Override
544 | public void onPageScrollStateChanged(int state) {
545 | if (mOnPageChangeListener != null) {
546 | mOnPageChangeListener.onPageScrollStateChanged(state);
547 | }
548 | // Log.i(tag,"currentItem: "+currentItem);
549 | switch (state) {
550 | case 0://No operation
551 | if (currentItem == 0) {
552 | viewPager.setCurrentItem(count, false);
553 | } else if (currentItem == count + 1) {
554 | viewPager.setCurrentItem(1, false);
555 | }
556 | break;
557 | case 1://start Sliding
558 | if (currentItem == count + 1) {
559 | viewPager.setCurrentItem(1, false);
560 | } else if (currentItem == 0) {
561 | viewPager.setCurrentItem(count, false);
562 | }
563 | break;
564 | case 2://end Sliding
565 | break;
566 | }
567 | }
568 |
569 | @Override
570 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
571 | if (mOnPageChangeListener != null) {
572 | mOnPageChangeListener.onPageScrolled(toRealPosition(position), positionOffset, positionOffsetPixels);
573 | }
574 | }
575 |
576 | @Override
577 | public void onPageSelected(int position) {
578 | currentItem=position;
579 | if (mOnPageChangeListener != null) {
580 | mOnPageChangeListener.onPageSelected(toRealPosition(position));
581 | }
582 | if (bannerStyle == BannerConfig.CIRCLE_INDICATOR ||
583 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE ||
584 | bannerStyle == BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE) {
585 | indicatorImages.get((lastPosition - 1 + count) % count).setImageResource(mIndicatorUnselectedResId);
586 | indicatorImages.get((position - 1 + count) % count).setImageResource(mIndicatorSelectedResId);
587 | lastPosition = position;
588 | }
589 | if (position == 0) position = count;
590 | if (position > count) position = 1;
591 | switch (bannerStyle) {
592 | case BannerConfig.CIRCLE_INDICATOR:
593 | break;
594 | case BannerConfig.NUM_INDICATOR:
595 | numIndicator.setText(position + "/" + count);
596 | break;
597 | case BannerConfig.NUM_INDICATOR_TITLE:
598 | numIndicatorInside.setText(position + "/" + count);
599 | bannerTitle.setText(titles.get(position - 1));
600 | break;
601 | case BannerConfig.CIRCLE_INDICATOR_TITLE:
602 | bannerTitle.setText(titles.get(position - 1));
603 | break;
604 | case BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE:
605 | bannerTitle.setText(titles.get(position - 1));
606 | break;
607 | }
608 |
609 | }
610 |
611 | @Deprecated
612 | public Banner setOnBannerClickListener(OnBannerClickListener listener) {
613 | this.bannerListener = listener;
614 | return this;
615 | }
616 |
617 | /**
618 | * 废弃了旧版接口,新版的接口下标是从1开始,同时解决下标越界问题
619 | *
620 | * @param listener
621 | * @return
622 | */
623 | public Banner setOnBannerListener(OnBannerListener listener) {
624 | this.listener = listener;
625 | return this;
626 | }
627 |
628 | public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
629 | mOnPageChangeListener = onPageChangeListener;
630 | }
631 |
632 | public void releaseBanner() {
633 | handler.removeCallbacksAndMessages(null);
634 | }
635 | }
636 |
--------------------------------------------------------------------------------