我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android实现自动变换大小的ViewPager

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android实现自动变换大小的ViewPager

前言

上一篇做了一个滑动折叠的Header控件,主要就是练习了一下滑动事件冲突的问题,控件和文章写的都不怎么样。本来想通过这篇文章的控件,整合一下前面六篇文章的内容的,结果写的太复杂了,就算了,没有新的技术知识,功能也和之前的安卓广东选择控件类似,不过在写的过程还是有点难度的,用来熟悉自定义view知识还是很不错的。

需求

这里我也不知道应该怎么描述这个控件,标题里用的大小自动变换的类ViewPager,一开始我把它叫做模仿桌面切换的多页面切换控件。大致就是和电视那种切换页面时,中间页面大,边上页面小,切换到中间会有变大的动画效果,我是觉得这样的控件和炫酷。

核心思想如下:

1、类似viewpager,但同时显示两种页面,中间为主页面,左右为小页面,小页面大小一样,间距排列

2、左右滑动可以将切换页面,超过页面数量大小不能滑动,滑动停止主界面能自动移动到目标位置

效果图

编写代码

这里代码写的还是挺简单的,没有用到ViewPager那样的Adapter,也没有处理预加载问题,滑动起来不是特别流畅,页面放置到顶层时切换很突兀,但是还是达到了一开始的设计要求吧!

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import androidx.core.animation.addListener
import androidx.core.view.children
import com.silencefly96.module_common.R
import java.util.*
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.pow
import kotlin.math.roundToInt

class DesktopLayerLayout @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ViewGroup(context, attributeSet, defStyleAttr) {
    companion object{
        // 方向
        const val ORIENTATION_VERTICAL = 0
        const val ORIENTATION_HORIZONTAL = 1
        // 状态
        const val SCROLL_STATE_IDLE = 0
        const val SCROLL_STATE_DRAGGING = 1
        const val SCROLL_STATE_SETTLING = 2
        // 默认padding值
        const val DEFAULT_PADDING_VALUE = 50
        // 竖向默认主界面比例
        const val DEFAULT_MAIN_PERCENT_VERTICAL = 0.8f
        // 横向默认主界面比例
        const val DEFAULT_MAIN_PERCENT_HORIZONTAL = 0.6f
        // 其他页面相对主界面页面最小的缩小比例
        const val DEFAULT_OTHER_VIEW_SCAN_SIZE = 0.5f
    }
    
    @Suppress("MemberVisibilityCanBePrivate")
    var curIndex = 0
    // 由于将view提高层级会搞乱顺序,需要记录原始位置信息
    private var mInitViews = ArrayList<View>()
    // view之间的间距
    private var mGateLength = 0
    // 滑动距离
    private var mDxLen = 0f
    // 系统最小移动距离
    private val mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
    // 控件状态
    private var mState = SCROLL_STATE_IDLE
    // 当前设置的属性动画
    private var mValueAnimator: ValueAnimator? = null
    // 实际布局的左右坐标值
    private var mRealLeft = 0
    private var mRealRight = 0
    // 上一次按下的横竖坐标
    private var mLastX = 0f
    // 方向,从XML内获得
    private var mOrientation: Int
    // 是否对屏幕方向自适应,从XML内获得
    private val isAutoFitOrientation: Boolean
    // padding,从XML内获得,如果左右移动,则上下要有padding,但左右没有padding
    private val mPaddingValue: Int
    // 竖向主内容比例,从XML内获得,剩余两边平分
    private val mMainPercentVertical: Float
    // 横向主内容比例,从XML内获得,剩余两边平分
    private val mMainPercentHorizontal: Float
    // 其他页面相对主界面页面最小的缩小比例
    private val mOtherViewScanMinSize: Float
    init {
        // 获取XML参数
        val typedArray =
            context.obtainStyledAttributes(attributeSet, R.styleable.DesktopLayerLayout)
        mOrientation = typedArray.getInteger(R.styleable.DesktopLayerLayout_mOrientation,
            ORIENTATION_VERTICAL)
        isAutoFitOrientation =
            typedArray.getBoolean(R.styleable.DesktopLayerLayout_isAutoFitOrientation, true)
        mPaddingValue = typedArray.getInteger(R.styleable.DesktopLayerLayout_mPaddingValue,
            DEFAULT_PADDING_VALUE)
        mMainPercentVertical =
            typedArray.getFraction(R.styleable.DesktopLayerLayout_mMainPercentVertical,
            1, 1, DEFAULT_MAIN_PERCENT_VERTICAL)
        mMainPercentHorizontal =
            typedArray.getFraction(R.styleable.DesktopLayerLayout_mMainPercentHorizontal,
            1, 1, DEFAULT_MAIN_PERCENT_HORIZONTAL)
        mOtherViewScanMinSize =
            typedArray.getFraction(R.styleable.DesktopLayerLayout_mOtherViewScanMinSize,
            1, 1, DEFAULT_OTHER_VIEW_SCAN_SIZE)
        typedArray.recycle()
    }
    override fun onFinishInflate() {
        super.onFinishInflate()
        // 获得所有xml内的view,保留原始顺序
        mInitViews.addAll(children)
    }
    // 屏幕方向变化并不会触发,初始时会触发,自适应
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // Log.e("TAG", "onSizeChanged: w=$w, h=$h")
        // 根据屏幕变化修改方向,自适应
        if (isAutoFitOrientation) {
            mOrientation = if (w > h) ORIENTATION_HORIZONTAL else ORIENTATION_VERTICAL
            requestLayout()
        }
    }
    // 需要在manifest中注册捕捉事件类型,android:configChanges="orientation|keyboardHidden|screenSize"
    public override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
            mOrientation = ORIENTATION_VERTICAL
            requestLayout()
        }else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mOrientation = ORIENTATION_HORIZONTAL
            requestLayout()
        }
    }
    // 排列规则:初始化第一个放中间,其他向右排列,中间最大,中心在左右边上的最小,不可见的也是最小
    // view的大小应该只和它在可见页面的位置有关,不应该和curIndex有关,是充分不必要关系
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        // 获取默认尺寸,考虑背景大小
        val width = max(getDefaultSize(0, widthMeasureSpec), suggestedMinimumWidth)
        val height = max(getDefaultSize(0, heightMeasureSpec), suggestedMinimumHeight)
        // 设置间距
        mGateLength = width / 4
        // 中间 view 大小
        val maxWidth: Int
        val maxHeight: Int
        // 不同方向尺寸不同
        if (mOrientation == ORIENTATION_HORIZONTAL) {
            maxWidth = (width * mMainPercentHorizontal).toInt()
            maxHeight = height - 2 * mPaddingValue
        }else {
            maxWidth = (width * mMainPercentVertical).toInt()
            maxHeight = height - 2 * mPaddingValue
        }
        // 两侧 view 大小,第三排
        val minWidth = (maxWidth * mOtherViewScanMinSize).toInt()
        val minHeight = (maxHeight * mOtherViewScanMinSize).toInt()
        var childWidth: Int
        var childHeight: Int
        for (i in 0 until childCount) {
            val child = mInitViews[i]
            val scanSize = getViewScanSize(i, scrollX)
            childWidth = minWidth + ((maxWidth - minWidth) * scanSize).toInt()
            childHeight = minHeight + ((maxHeight - minHeight) * scanSize).toInt()
            // Log.e("TAG", "onMeasure($i): childWidth=$childWidth, childHeight=$childHeight")
            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY))
        }
        setMeasuredDimension(width, height)
    }
    // 选中view为最大,可见部分会缩放,不可见部分和第三排一样大
    private fun getViewScanSize(index: Int, scrolledLen: Int): Float {
        var scanSize = 0f
        // 开始时当前view未测量,不计算
        if (measuredWidth == 0) return scanSize
        // 初始化的时候,第一个放中间,所以index移到可见范围为[2+index, index-2],可见!=可移动
        val scrollLeftLimit = (index - 2) * mGateLength
        val scrollRightLimit = (index + 2) * mGateLength
        // 先判断child是否可见
        if (scrolledLen in scrollLeftLimit..scrollRightLimit) {
            // 根据二次函数计算比例
            scanSize = scanByParabola(scrollLeftLimit, scrollRightLimit, scrolledLen).toFloat()
        }
        return scanSize
    }
    // 根据抛物线计算比例,y属于[0, 1]
    // 映射关系:(form, 0) ((from + to) / 2, 0) (to, 0) -> (0, 0) (1, 1) (2, 0)
    @Suppress("SameParameterValue")
    private fun scanByParabola(from: Int, to: Int, cur: Int): Double {
        // 公式:val y = 1 - (x - 1).toDouble().pow(2.0)
        // Log.e("TAG", "scanByParabola:from=$from, to=$to, cur=$cur ")
        val x = ((cur - from) / (to - from).toFloat() * 2).toDouble()
        return 1 - (x - 1).pow(2.0)
    }
    // layout 按顺序间距排列即可,大小有onMeasure控制,开始位置在中心,也和curIndex无关
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val startX = (r + l) / 2
        // 排列布局
        for (i in 0 until childCount) {
            val child = mInitViews[i]
            // 中间减去间距,再减去一半的宽度,得到左边坐标
            val left = startX + mGateLength * i - child.measuredWidth / 2
            val top = (b + t) / 2 - child.measuredHeight / 2
            val right = left + child.measuredWidth
            val bottom = top + child.measuredHeight
            // Log.e("TAG", "onLayout($i): left=$left, right=$right")
            child.layout(left, top, right, bottom)
        }
        // 修改大小,布局完成后移动
        scrollBy(mDxLen.toInt(), 0)
        mDxLen = 0f
        // 完成布局及移动后,绘制之前,将可见view提高层级
        val targetIndex = getCurrentIndex()
        for (i in 2 downTo 0) {
            val preIndex = targetIndex - i
            val aftIndex = targetIndex + i
            // 逐次提高层级,注意在mInitViews拿就可以,不可见不管
            if (preIndex in 0..childCount) {
                bringChildToFront(mInitViews[preIndex])
            }
            if (aftIndex != preIndex && aftIndex in 0 until childCount) {
                bringChildToFront(mInitViews[aftIndex])
            }
        }
    }
    // 根据滚动距离获得当前index
    private fun getCurrentIndex()= (scrollX / mGateLength.toFloat()).roundToInt()
    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        ev?.let {
            when(it.action) {
                MotionEvent.ACTION_DOWN -> {
                    mLastX = ev.x
                    if(mState == SCROLL_STATE_IDLE) {
                        mState = SCROLL_STATE_DRAGGING
                    }else if (mState == SCROLL_STATE_SETTLING) {
                        mState = SCROLL_STATE_DRAGGING
                        // 去除结束监听,结束动画
                        mValueAnimator?.removeAllListeners()
                        mValueAnimator?.cancel()
                    }
                }
                MotionEvent.ACTION_MOVE -> {
                    // 若ACTION_DOWN是本view拦截,则下面代码不会触发,要在onTouchEvent判断
                    val dX = mLastX - ev.x
                    return checkScrollInView(scrollX + dX)
                }
                MotionEvent.ACTION_UP -> {}
            }
        }
        return super.onInterceptHoverEvent(ev)
    }
    // 根据可以滚动的范围,计算是否可以滚动
    private fun checkScrollInView(length : Float): Boolean {
        // 一层情况
        if (childCount <= 1) return false
        // 左右两边最大移动值,即把最后一个移到中间
        val leftScrollLimit = 0
        val rightScrollLimit = (childCount - 1) * mGateLength
        return (length >= leftScrollLimit && length <= rightScrollLimit)
    }
    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(ev: MotionEvent?): Boolean {
        ev?.let {
            when(it.action) {
                // 防止点击空白位置或者子view未处理touch事件
                MotionEvent.ACTION_DOWN -> return true
                MotionEvent.ACTION_MOVE -> {
                    // 如果是本view拦截的ACTION_DOWN,要在此判断
                    val dX = mLastX - ev.x
                    if(checkScrollInView(scrollX + dX)) {
                        move(ev)
                    }
                }
                MotionEvent.ACTION_UP -> moveUp()
            }
        }
        return super.onTouchEvent(ev)
    }
    private fun move(ev: MotionEvent) {
        val dX = mLastX - ev.x
        // 修改mScrollLength,重新measure及layout,再onLayout的最后实现移动
        mDxLen += dX
        if(abs(mDxLen) >= mTouchSlop) {
            requestLayout()
        }
        // 更新值
        mLastX = ev.x
    }
    private fun moveUp() {
        // 赋值
        val targetScrollLen = getCurrentIndex() * mGateLength
        // 不能使用scroller,无法在移动的时候进行测量
        // mScroller.startScroll(scrollX, scrollY, (targetScrollLen - scrollX), 0)
        // 这里使用ValueAnimator处理剩余的距离,模拟滑动到需要的位置
        val animator = ValueAnimator.ofFloat(scrollX.toFloat(), targetScrollLen.toFloat())
        animator.addUpdateListener { animation ->
            // Log.e("TAG", "stopMove: " + animation.animatedValue as Float)
            mDxLen = animation.animatedValue as Float - scrollX
            requestLayout()
        }
        // 在动画结束时修改curIndex
        animator.addListener (onEnd = {
            curIndex = getCurrentIndex()
            mState = SCROLL_STATE_IDLE
        })
        // 设置状态
        mState = SCROLL_STATE_SETTLING
        animator.duration = 300L
        animator.start()
    }
}

desktop_layer_layout_style.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name ="DesktopLayerLayout">
        <attr name="mOrientation">
            <enum name ="vertical" value="0" />
            <enum name ="horizontal" value="1" />
        </attr>
        <attr name="isAutoFitOrientation" format="boolean"/>
        <attr name="mPaddingValue" format="integer"/>
        <attr name="mMainPercentVertical" format="fraction"/>
        <attr name="mMainPercentHorizontal" format="fraction"/>
        <attr name="mOtherViewScanMinSize" format="fraction"/>
    </declare-styleable>
</resources>

主要问题

这里用到的知识之前六篇文章都已经讲过了,主要就是有几点实现起来复杂了一些,下面讲讲。

页面的自动缩放

讲解页面的缩放之前,需要先将一下页面的摆放。这里以四分之一为间距来摆放来自XML的view,第一个view放在中间,其他都在其右边按顺序排列。

所以页面的缩放,只和view的位置有关,而view的位置又只和当前控件左右滑动的距离有关,变量就是当前控件横坐标上的滑动值scrollX。根据view的原始index可以得到每个view可见时的滑动值范围,在通过这个范围和实际的滑动值scrollX,进行映射换算得到其缩放比例。这里用到了抛物线进行换算:

// 公式:y = 1 - (x - 1).toDouble().pow(2.0)
// 映射关系:(form, 0) ((from + to) / 2, 0) (to, 0) -> (0, 0) (1, 1) (2, 0)

滑动范围的限定

滑动范围的限定和上面类似,边界就是第一个或者最后一个view移动到正中间的范围,只要实际的滑动值scrollX在这个范围内,那滑动就是有效的。

页面层级提升与恢复

页面层级的提升在我之前文章:手撕安卓侧滑栏也有用到,就是自己把view放到children的最后去,实际上ViewGroup提供了类似的功能:bringChildToFront,但是原理是一样的。

    @Override
    public void bringChildToFront(View child) {
        final int index = indexOfChild(child);
        if (index >= 0) {
            removeFromArray(index);
            addInArray(child, mChildrenCount);
            child.mParent = this;
            requestLayout();
            invalidate();
        }
    }

这里的提升view不止一个了,而且后面还要恢复,即不能打乱children的顺序。所以我在onFinishInflate中用一个数组保存下这些子view的原始顺序,使用的时候用这个数组就行,children里面的顺序不用管,只要让需要显示的view放在最后就行。我这里因为间距是四分之一的宽度,最多可以显示五个view,所以在onLayout的最后将这五个view得到,并按顺序放到children的最后。

onDraw探讨

这里我还想对onDraw探讨一下,一开始我以为既然onMeasure、onLayout中都需要去调用child的measure和layout,那能不能在onDraw里面自己去绘制child,不用自带的,结果发现这是不行的。onDraw实际是View里面的一个空方法,实际对页面的绘制是在控件的draw方法中,那重写draw方法自己去绘制child呢?实际也不行,当把draw方法里面的super.draw时提示报错:

也就是说必须继承super.draw这个方法,点开源码发现,super.draw已经把child绘制了,而且onDraw方法也是从里面传出来的。所以没办法,乖乖用bringChildToFront放到children最后去,来提升层级吧,不然也不会提供这一个方法来是不是?

到此这篇关于Android实现自动变换大小的ViewPager的文章就介绍到这了,更多相关Android ViewPager内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android实现自动变换大小的ViewPager

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android实现自动变换大小的ViewPager

ViewPager使用适配器类将数据和view的处理分离,ViewPager的适配器叫PagerAdapter,这是一个抽象类,不能实例化,所以它有两个子类:FragmentPagerAdapter和FragmentStatePagerAdapter,这两个都是处理页面为Fragment的情况
2022-11-13

Android实现自动变换大小的组件ViewPager2

这篇文章主要介绍了Android实现自动变换大小的组件ViewPager2,ViewPager2最显著的特点是基于RecyclerView实现,RecyclerView是目前Android端最成熟的AdapterView解决方案
2023-03-20

Android如何实现自动变换大小的组件ViewPager2

本篇内容介绍了“Android如何实现自动变换大小的组件ViewPager2”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!ViewPager
2023-07-05

Android实现千变万化的ViewPager切换动画

之前写过一篇文章:Android自定义ViewPager实现个性化的图片切换效果,有朋友提出,ViewPager自带了一个setPageTransformer用于设置切换动画~ 本篇文章,将进行以下内容学习: 1、介绍如何使用setPage
2022-06-06

Android App中用Handler实现ViewPager页面的自动切换

在很多电商网页及app上都有自动切换的商品的推广快,感觉体验挺不错的,正好今天学习使用ViewPager,因此也实现了一个功能类似的demo。 下面是其中的两个截图:实现一个自动播放功能的ViewPager,要做的主要有以下的几个部分: 实
2022-06-06

Android ViewPager实现动画切换效果

概述 ViewPager是Android开发中使用场景非常频繁的控件,单一的动画效果切换已经越来越不能满足追求个性化的应用中。而ViewPager自身也带有一个接口来处理页面间的动画切换,那就是setPageTransformer。下面我们
2022-06-06

Android使用ViewPager实现自动轮播

很多APP中都实现了类似引导页的自动轮播,不由得想到昨天的引导页上修改一下代码实现轮播。 其实大体上只需要添加一个线程循环执行就可以了。 项目已同步至:https://github.com/nanchen2251/viewpagerDemo
2022-06-06

Android简单实现无限滚动自动滚动的ViewPager

经常我们会在应用中看到一个可以自动滚动,并且无限滚动的一个ViewPager,百度谷歌上面也有很多关于这方面的教程,但是感觉都略显麻烦,而且封装的都不是很彻底。所以试着封装一个比较好用的ViewPager 效果如下:简单的说一下实现思路,要
2022-06-06

Android利用ViewPager实现可滑动放大缩小画廊效果

画廊在很多的App设计中都有,如下图所示:该例子是我没事的时候写的一个小项目,具体源码地址请访问https://github.com/AlexSmille/YingMi。 该画廊类似封面的效果,滑到中间的图片会慢慢变大,离开的View会慢慢
2022-06-06

Android实现带指示器的自动轮播式ViewPager

前言 最近在做项目的时候,有个需求就是实现自动轮播式的ViewPager,最直观的例子就是知乎日报顶部的ViewPager,它内部有着好几个子view,每个一段时间便自动滑动到下一个item view,而底部的指示器也随之跟着改变。使用这种
2022-06-06

Android自定义ViewPager实现个性化的图片切换效果

第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主界面通通ViewPager,以及图片切换也抛弃了ImageSwitch之类的,开始让ViewPager来做。时间长了,ViewPager的切换效果觉得枯燥,形成了审美疲劳~~我们
2022-06-06

Android开发中实现一个头像滑动变大变小功能

这篇文章给大家介绍Android开发中实现一个头像滑动变大变小功能,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。实现通过监听ScrollView在Y轴的滑动距离,然后在代码中动态设置头像的位置和大小。public cl
2023-05-31

Android ViewPager无限循环实现底部小圆点动态滑动

页面拖动到最后一页 再向下滑动回复到 第一页,第一页向前滑动回到 最后一页 同时,底部红色小圆点随着页面的滑动距离比例随时改变位置布局:
2022-06-06

Android实现图片自动切换

在Android中图片的自动切换不仅可以实现自动切换,而且还可以使用手动切换。而且一般在切换的时候,在图片下方还带有其他内容的切换,用来标记是第几个图片的切换。 实现效果如下:具体的示例代码如下: 布局代码:
2022-06-06

Android App中使用ViewPager+Fragment实现滑动切换效果

在android应用中,多屏滑动是一种很常见的风格,没有采用viewpager的代码实现会很长,如果采用ViewPager,代码就会短很多,但是使用ViewPager也有弊端:需要导入android-support-v4.jar、细节无法控
2022-06-06

Android ViewPager撤消左右滑动切换功能实现代码

最近做项目要求某种情况下ViewPager不能滑动,那么我们只需要重写这个方法就可以禁止ViewPager滑动。下面通过本文给大家ViewPager取消左右滑动切换功能的实例代码,具体代码如下所示: IndexViewPager.Java:
2022-06-06

Android实现实时滑动ViewPager的2种方式

先看看效果图:activity_main.xml 2022-06-06

Android 自定义布局竖向的ViewPager的实现

Android 自定义布局竖向的ViewPager的实现效果图:这个自定义控件涉及到的知识点:自定义ViewGroup中onMeasure和onLayout的写法 弹性滚动Scroller的用法 速度轨迹追踪器VelocityTracker
2023-05-31

Android 使用ViewPager实现图片左右循环滑动自动播放

ViewPager这个小demo实现的是可以左右循环滑动图片,下面带索引,滑到最后一页在往右滑动就要第一页,第一页往左滑动就到最后一页,先上效果图,用美女图片是我一贯的作风,呵呵1. 首先看一些layout下的xml
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录