android开发之做一个竖着的seekbar

工作繁忙,总想有机会停下来喘口气,整理一下思路,做一些积累,但是这样的机会很少。要做android的界面,基础的要求是熟悉那些内置的组件(prebuilt componments),而有时候这些组件是不够用的,这时候我们就需要自己写一个新的,或者修改内置的。这就要求我们了解更多知识,比如各个View是怎么画出来的,UI上各种事件是如何分发和处理的等等。这些我都计划写出来和大家分享,网络上中文资料,

大家好,又见面了,我是全栈君。

  工作繁忙,总想有机会停下来喘口气,整理一下思路,做一些积累,但是这样的机会很少。要做android的界面,基础的要求是熟悉那些内置的组件(prebuilt componments),而有时候这些组件是不够用的,这时候我们就需要自己写一个新的,或者修改内置的。这就要求我们了解更多知识,比如各个View是怎么画出来的,UI上各种事件是如何分发和处理的等等。这些我都计划写出来和大家分享,网络上中文资料,说得清楚的太少了,就那么几篇被广泛的转载。今天说一说如何做一个vertical seekbar,竖起来的seekbar。实现它的过程中就会涉及一些View是如何被画出来的知识。通过本文你可以实现一个verticalseekbar,你也可以学到一些使用seekbar的知识。

     我的一个同事很牛,需要自定义的组件时,从framework中抠出来相关(功能相似、在继承关系的hierarchy中地位相当)的代码(类),在自己的应用程序中做修改,实现自己想要的功能。需要的style、drawable等资源,有些是在应用程序里用不了的,就也自己做一份。我们公司做整个系统,从硬件到android到android上的应用系统, framework我们也修改,主要是功能上的增减。在framework里增加一个verticalseekbar我们也能做到,但是这样兼容性就被破坏了,所以这位仁兄把需要的拿出来在自己的程序中实现。

     我做vertical seekbar也采取这个策略,不和大家啰嗦实现过程中太多因果逻辑,我想很多读者读这篇文章就是为了做出来一个vertical seekbar。为了方便手头没有代码和开发环境的读者,我会贴上framework中的相关代码。

     vertical seekbar当然要实现seekbar一样的功能,所以首先seekbar有的vertical seekbar都要有,直接在你的工程中新建一个类vertical seekbar,从framework中把seekbar的代码复制过来,做一些必要的修改。我们来看看seekbar做了些什么.

seekbar代码:

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.widget;
import android.content.Context;
import android.util.AttributeSet;
/**
 * A SeekBar is an extension of ProgressBar that adds a draggable thumb. The user can touch
 * the thumb and drag left or right to set the current progress level or use the arrow keys.
 * Placing focusable widgets to the left or right of a SeekBar is discouraged. 
 * <p>
 * Clients of the SeekBar can attach a {@link SeekBar.OnSeekBarChangeListener} to
 * be notified of the user's actions.
 *
 * @attr ref android.R.styleable#SeekBar_thumb
 */
public class SeekBar extends AbsSeekBar {
    /**
     * A callback that notifies clients when the progress level has been
     * changed. This includes changes that were initiated by the user through a
     * touch gesture or arrow key/trackball as well as changes that were initiated
     * programmatically.
     */
    public interface OnSeekBarChangeListener {
        
        /**
         * Notification that the progress level has changed. Clients can use the fromUser parameter
         * to distinguish user-initiated changes from those that occurred programmatically.
         * 
         * @param seekBar The SeekBar whose progress has changed
         * @param progress The current progress level. This will be in the range 0..max where max
         *        was set by {@link ProgressBar#setMax(int)}. (The default value for max is 100.)
         * @param fromUser True if the progress change was initiated by the user.
         */
        void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser);
    
        /**
         * Notification that the user has started a touch gesture. Clients may want to use this
         * to disable advancing the seekbar. 
         * @param seekBar The SeekBar in which the touch gesture began
         */
        void onStartTrackingTouch(SeekBar seekBar);
        
        /**
         * Notification that the user has finished a touch gesture. Clients may want to use this
         * to re-enable advancing the seekbar. 
         * @param seekBar The SeekBar in which the touch gesture began
         */
        void onStopTrackingTouch(SeekBar seekBar);
    }
    private OnSeekBarChangeListener mOnSeekBarChangeListener;
    
    public SeekBar(Context context) {
        this(context, null);
    }
    
    public SeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.seekBarStyle);
    }
    public SeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    @Override
    void onProgressRefresh(float scale, boolean fromUser) {
        super.onProgressRefresh(scale, fromUser);
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
        }
    }
    /**
     * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also
     * provides notifications of when the user starts and stops a touch gesture within the SeekBar.
     * 
     * @param l The seek bar notification listener
     * 
     * @see SeekBar.OnSeekBarChangeListener
     */
    public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
        mOnSeekBarChangeListener = l;
    }
    
    @Override
    void onStartTrackingTouch() {
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onStartTrackingTouch(this);
        }
    }
    
    @Override
    void onStopTrackingTouch() {
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onStopTrackingTouch(this);
        }
    }
    
}

 

它定义了一个接口OnSeekBarChangeListener,接口中定义了三个方法:

        void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser);
        void onStartTrackingTouch(SeekBar seekBar);
        void onStopTrackingTouch(SeekBar seekBar);

看名字也知道它们是做什么的,代码也给了注释。那么它们是怎么做到的呢?seekbar中声明了一个该接口的对象mOnSeekBarChangeListener,方法setOnSeekBarChangeListener()将给它赋值。在你的程序中定义一个对象,实现OnSeekBarChangeListener,并调用setOnSeekBarChangeListener()把这个对象传给mOnSeekBarChangeListener。在seekbar中还有几个override的方法:

    void onProgressRefresh(float scale, boolean fromUser)

    void onStartTrackingTouch() 
    void onStopTrackingTouch()

这些方法会在发生了相应事件的时候被调用,而它们又相应地去调用mOnSeekBarChangeListener中的方法,于是你的程序的定义的响应各种事件的方法就被执行了。

 

但是这些还不够,我们要让seekbar竖起来,要让thumb上下滑动,实现这些特性的代码在什么地方呢?我们顺着seekbar的继承关系向上找。来看看absseekbar。

absseekbar的代码:

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
public abstract class AbsSeekBar extends ProgressBar {
    private Drawable mThumb;
    private int mThumbOffset;
    
    /**
     * On touch, this offset plus the scaled value from the position of the
     * touch will form the progress value. Usually 0.
     */
    float mTouchProgressOffset;
    /**
     * Whether this is user seekable.
     */
    boolean mIsUserSeekable = true;
    /**
     * On key presses (right or left), the amount to increment/decrement the
     * progress.
     */
    private int mKeyProgressIncrement = 1;
    
    private static final int NO_ALPHA = 0xFF;
    private float mDisabledAlpha;
    
    public AbsSeekBar(Context context) {
        super(context);
    }
    public AbsSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public AbsSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.SeekBar, defStyle, 0);
        Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
        setThumb(thumb);
        int thumbOffset =
                a.getDimensionPixelOffset(com.android.internal.R.styleable.SeekBar_thumbOffset, 0);
        setThumbOffset(thumbOffset);
        a.recycle();
        a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.Theme, 0, 0);
        mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.Theme_disabledAlpha, 0.5f);
        a.recycle();
    }
    /**
     * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar
     * 
     * @param thumb Drawable representing the thumb
     */
    public void setThumb(Drawable thumb) {
        if (thumb != null) {
            thumb.setCallback(this);
        }
        mThumb = thumb;
        invalidate();
    }
    /**
     * @see #setThumbOffset(int)
     */
    public int getThumbOffset() {
        return mThumbOffset;
    }
    /**
     * Sets the thumb offset that allows the thumb to extend out of the range of
     * the track.
     * 
     * @param thumbOffset The offset amount in pixels.
     */
    public void setThumbOffset(int thumbOffset) {
        mThumbOffset = thumbOffset;
        invalidate();
    }
    /**
     * Sets the amount of progress changed via the arrow keys.
     * 
     * @param increment The amount to increment or decrement when the user
     *            presses the arrow keys.
     */
    public void setKeyProgressIncrement(int increment) {
        mKeyProgressIncrement = increment < 0 ? -increment : increment;
    }
    /**
     * Returns the amount of progress changed via the arrow keys.
     * <p>
     * By default, this will be a value that is derived from the max progress.
     * 
     * @return The amount to increment or decrement when the user presses the
     *         arrow keys. This will be positive.
     */
    public int getKeyProgressIncrement() {
        return mKeyProgressIncrement;
    }
    
    @Override
    public synchronized void setMax(int max) {
        super.setMax(max);
        if ((mKeyProgressIncrement == 0) || (getMax() / mKeyProgressIncrement > 20)) {
            // It will take the user too long to change this via keys, change it
            // to something more reasonable
            setKeyProgressIncrement(Math.max(1, Math.round((float) getMax() / 20)));
        }
    }
    @Override
    protected boolean verifyDrawable(Drawable who) {
        return who == mThumb || super.verifyDrawable(who);
    }
    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        
        Drawable progressDrawable = getProgressDrawable();
        if (progressDrawable != null) {
            progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
        }
        
        if (mThumb != null && mThumb.isStateful()) {
            int[] state = getDrawableState();
            mThumb.setState(state);
        }
    }
    
    @Override
    void onProgressRefresh(float scale, boolean fromUser) { 
        Drawable thumb = mThumb;
        if (thumb != null) {
            setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
            /*
             * Since we draw translated, the drawable's bounds that it signals
             * for invalidation won't be the actual bounds we want invalidated,
             * so just invalidate this whole view.
             */
            invalidate();
        }
    }
    
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Drawable d = getCurrentDrawable();
        Drawable thumb = mThumb;
        int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
        // The max height does not incorporate padding, whereas the height
        // parameter does
        int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
        
        int max = getMax();
        float scale = max > 0 ? (float) getProgress() / (float) max : 0;
        
        if (thumbHeight > trackHeight) {
            if (thumb != null) {
                setThumbPos(w, thumb, scale, 0);
            }
            int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
            if (d != null) {
                // Canvas will be translated by the padding, so 0,0 is where we start drawing
                d.setBounds(0, gapForCenteringTrack, 
                        w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack
                        - mPaddingTop);
            }
        } else {
            if (d != null) {
                // Canvas will be translated by the padding, so 0,0 is where we start drawing
                d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
                        - mPaddingTop);
            }
            int gap = (trackHeight - thumbHeight) / 2;
            if (thumb != null) {
                setThumbPos(w, thumb, scale, gap);
            }
        }
    }
    /**
     * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and
     */
    private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
        int available = w - mPaddingLeft - mPaddingRight;
        int thumbWidth = thumb.getIntrinsicWidth();
        int thumbHeight = thumb.getIntrinsicHeight();
        available -= thumbWidth;
        // The extra space for the thumb to move on the track
        available += mThumbOffset * 2;
        int thumbPos = (int) (scale * available);
        int topBound, bottomBound;
        if (gap == Integer.MIN_VALUE) {
            Rect oldBounds = thumb.getBounds();
            topBound = oldBounds.top;
            bottomBound = oldBounds.bottom;
        } else {
            topBound = gap;
            bottomBound = gap + thumbHeight;
        }
        
        // Canvas will be translated, so 0,0 is where we start drawing
        thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
    }
    
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mThumb != null) {
            canvas.save();
            // Translate the padding. For the x, we need to allow the thumb to
            // draw in its extra space
            canvas.translate(mPaddingLeft - mThumbOffset, mPaddingTop);
            mThumb.draw(canvas);
            canvas.restore();
        }
    }
    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Drawable d = getCurrentDrawable();
        int thumbHeight = mThumb == null ? 0 : mThumb.getIntrinsicHeight();
        int dw = 0;
        int dh = 0;
        if (d != null) {
            dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
            dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
            dh = Math.max(thumbHeight, dh);
        }
        dw += mPaddingLeft + mPaddingRight;
        dh += mPaddingTop + mPaddingBottom;
        
        setMeasuredDimension(resolveSize(dw, widthMeasureSpec),
                resolveSize(dh, heightMeasureSpec));
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!mIsUserSeekable || !isEnabled()) {
            return false;
        }
        
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                setPressed(true);
                onStartTrackingTouch();
                trackTouchEvent(event);
                break;
                
            case MotionEvent.ACTION_MOVE:
                trackTouchEvent(event);
                attemptClaimDrag();
                break;
                
            case MotionEvent.ACTION_UP:
                trackTouchEvent(event);
                onStopTrackingTouch();
                setPressed(false);
                break;
                
            case MotionEvent.ACTION_CANCEL:
                onStopTrackingTouch();
                setPressed(false);
                break;
        }
        return true;
    }
    private void trackTouchEvent(MotionEvent event) {
        final int width = getWidth();
        final int available = width - mPaddingLeft - mPaddingRight;
        int x = (int)event.getX();
        float scale;
        float progress = 0;
        if (x < mPaddingLeft) {
            scale = 0.0f;
        } else if (x > width - mPaddingRight) {
            scale = 1.0f;
        } else {
            scale = (float)(x - mPaddingLeft) / (float)available;
            progress = mTouchProgressOffset;
        }
        
        final int max = getMax();
        progress += scale * max;
        
        setProgress((int) progress, true);
    }
    /**
     * Tries to claim the user's drag motion, and requests disallowing any
     * ancestors from stealing events in the drag.
     */
    private void attemptClaimDrag() {
        if (mParent != null) {
            mParent.requestDisallowInterceptTouchEvent(true);
        }
    }
    
    /**
     * This is called when the user has started touching this widget.
     */
    void onStartTrackingTouch() {
    }
    /**
     * This is called when the user either releases his touch or the touch is
     * canceled.
     */
    void onStopTrackingTouch() {
    }
    /**
     * Called when the user changes the seekbar's progress by using a key event.
     */
    void onKeyChange() {
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        int progress = getProgress();
        
        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_LEFT:
                if (progress <= 0) break;
                setProgress(progress - mKeyProgressIncrement, true);
                onKeyChange();
                return true;
        
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                if (progress >= getMax()) break;
                setProgress(progress + mKeyProgressIncrement, true);
                onKeyChange();
                return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

 

   连上注释372行,不多。我们看到有onTouchEvent(),trackTouchEvent(),onProgressRefresh(),setThumbPos()等一些方法,这些都是和seekbar功能相关的方法,去看看它们的注释。

     onTouchEvent()是在View中就有定义的方法,(touch)事件处理我日后有空我会再发文章讨论,现在我们只要知道,在用户触摸seekbar(absseekbar)时,这个方法会响应touch事件就够了。onTouchEvent()会判断用户的动作,然后调用前述的一些方法(注意去看代码),注意它调用的顺序,判断是ACTION_DOWN时先调用onStartTrackingTouch()再调用trackTouchEvent(event),于是在你的程序中定义的 onStartTrackingTouch()先执行了;判断是ACTION_UP时先调用trackTouchEvent(event)再调用onStopTrackingTouch(),至于为什么我就不啰嗦了,有时候这个顺序是很重要的。

     trackTouchEvent()根据touch的位置来设置进度,它计算出progress后调用setProgress(),setProgress()是progressbar中实现的方法。

     当seekbar的progress改变时onProgressRefresh()会被调用,它会调用setThumbPos()来设置thumb的位置。

     大致过程就是这样,要更深入细致的了解还是得认真得看看代码。

     现在我们知道,seekbar拥有的progressbar之外的功能,都是在absseekbar中实现的。系统默认seekbar是横着的,在absseekbar中计算progress、计算thumb的位置,用到了seekbar的宽和高,我们只要改变一下计算方法就能计算出竖着的seekbar所需要的各个数值。例如:

    private void trackTouchEvent(MotionEvent event) {

        final int Height = getHeight();
        Log.d(“demon”, “Y:”+event.getY()+” height:”+Height);
        final int available = Height – getPaddingBottom() – getPaddingTop();
        int Y = (int)event.getY();
        float scale;
        float progress = 0;
        if (Y > Height – getPaddingBottom()) {

            scale = 0.0f;
        } else if (Y  < getPaddingTop()) {

            scale = 1.0f;
        } else {

            scale = (float)(Height – getPaddingBottom()-Y) / (float)available;
        }
        
        final int max = getMax();
        progress = scale * max;        
        setProgress((int) progress);
    }

     其他的地方你可以仿造这个写。

     功能上分析告一段落,我们如何画出来一个竖着的seekbar呢?

     我想了两个办法,一是自己画,也就是自己写个draw,太难,而且很难和系统风格统一。另外一个办法是让系统给我们画。verticalseekbar只是seekbar转了90度或-90度,我们可以把画布转一个角度,然后交给系统去画,具体的做法就是在ondraw()时调整画布,然后调用super.onDraw()。

     这个调整也就是旋转和平移。至于旋转和平移的具体实现,我跟到native部分就没有看下去了,也没有看到具体的介绍。我的理解,你要保证画布的左上角在旋转平移以后坐标不变,否者会出现很多问题。 就我们的seekbar而言,如果你要获得一个向上增长的seekbar,那么代码应该是:       

protected void onDraw(Canvas c)
        {

                c.rotate(-90);
                c.translate(-height,0);//height是你的verticalseekbar的高
                super.onDraw(c);
        }

如果是向下的seekbar则应该是:

protected void onDraw(Canvas c)
        {

                c.rotate(90);
                c.translate(0,-width);//width是你的verticalseekbar的宽
                super.onDraw(c);
        }

你也可以自己计算一下来验证。

     一个View在屏幕上画出来,首先measure()会被调用,这是个final方法,measure()会调用onMeasure()。你可以重写onMeasure(),但是重写的onMeasure()中必须调用setMeasuredDimension(int,int), setMeasuredDimension()会保存该View的长和宽。我们可以重写onMeasure()方法给我们的seekbar分配一块竖直的,长宽由我们设定的区域。在画seekbar之前,我们把这个区域旋转了90度交给系统,系统给我们画一个标准的seekbar,在整个layout中,这个seekbar就成竖直的了。

    实际操作时,我们在工程中新建了类verticalSeekbar,把framework中seekbar的代码拷贝过来,做一些必须的修改(类名、构造方法什么的)。注意如果我们的verticalseekbar是在XML中定义的,在代码中使用findViewById()方法来获得,系统将使用第二个构造方法(我简单地试了一下,没有看到官方资料或权威的解释),也就是

    public SeekBar(Context context, AttributeSet attrs) {

        this(context, attrs, com.android.internal.R.attr.seekBarStyle);
    }

com.android.internal.R.attr.seekBarStyle在我我们的应用程序中是无法使用的,你可以换成android.R.attr.seekBarStyle,你也可以自己定义一个,但是必须包含系统本身的。

    然后我们要加一些方法,也就是重写absseekbar中的一些方法,大致如下:

 

verticalSeekbar.java的大致内容

 

由于是给公司写的代码,不便共享,望大家见谅。贴俩实现了的图,鼓舞一下对verticalseekbar有需求的读者——做一个verticalseekbar不难。

自上向下的verticalseekbar 自下向上的verticalseekbar

欢迎收藏,不要盲目转载,你验证了我说的,在传播给别人。

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/121379.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Python编写的memcached启动脚本

    Python编写的memcached启动脚本

  • node读取文件操作

    node读取文件操作导言:nodejs中所有与文件相关的操作都在fs模块中,而读写操作又是我们会经常用到的操作,nodejs的fs模块针对读操作为我们提供了readFile,read,createReadStream三个方法,针对写操作为我们提供了writeFile,write,createWriteStream三个方法,下面分析一下它们的区别:一、readFile和writeFile1、readFile…

  • 差分数组技巧

    差分数组技巧一、差分数组适用题型,和技巧前缀和数组:适用于原始数组不会被修改的情况下,频繁查询某个区间的累加和差分数组:主要适⽤场景是频繁对原始数组的某个区间的元素进⾏增减(比如:给你和数组arr,然后再下标0-4之间各元素加一,2-5之间各个元素减2,求最终的原数组)差分数组技巧1.构建差分数组(diff),diff[0]=nums[0],之后diff[i]=nums[i]-nums[i-1]int[]diff=newint[nums.length];//构造差分数组diff[0]=n

  • suse linux rpm 安装

    suse linux rpm 安装安装rpm包时遇到error:Faileddependencies:错误在linux下安装rpm包时经常会遇到下面这个问题:error:Faileddependencies:……………………………………………..省略号表示安装此rpm缺少的包。遇到此问题时可以在安装rpm包命令的后面加两个参数,如:

  • JAVA——Tess4J简单的图像识别DEMO

    JAVA——Tess4J简单的图像识别DEMO基本概念TesseractOpenSourceOCREngine:包含一个OCR引擎-libtesseract和一个命令行程序-tesseract。Tesseract4添加了一个新的基于LSTM的OCR引擎,该引擎专注于行识别,但仍支持Tesseract3的传统TesseractOCR引擎,该引擎通过识别字符模式进行工作。通过使用传统OCR引擎模式(–oem0),可以与Tesseract3兼容。它还需要训练有素的数据文件来支持旧式引擎,例如tessdata存储库中的文件。…

  • 微信小程序–单选复选按钮组的实现

    微信小程序–单选复选按钮组的实现本文主要介绍微信小程序单选按钮很多选按钮的实现方式。众所周知,小程序目前无法绑定DOM截点,实现的原理就是通过bindtap点击的事件方法获取data-id进行循环遍历取反而实现该效果。(一)单选按钮组模型图如下:index.jsPage({data:{parameter:[{id:1,name:’银色’},{id:2,name:’白色’},{i

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号