A sliding scoring control

This time it brings a sliding scoring control. When doing the scoring function, you often need a control to drag a finger to score. In fact, this kind of control has a lot of 100 degrees. I wrote one a long time ago, and I'll share it with you here.


The control is named SlideToGradeLayout. There is only one file. Copy and paste are available. The control ontology is as follows:

SlideToGradeLayout.java:

public class SlideToGradeLayout extends LinearLayout {
    private Context context;

    // The width of the stars
    private float iconWidth = 20;

    // The height of the stars
    private float iconHeight = 20;

    // Star spacing
    private float iconPadding = 5;

    // Current score
    private float currentScore = 0;

    // Number of stars
    private int iconCount = 5;

    // Whether to lock sliding? If it is true, only scores will be displayed, and sliding has no response
    private boolean isLock = false;

    // Initialized or not
    private boolean isInit = false;

    // Only integer stars are allowed. If it is false, half stars can be played
    private boolean isIntegerOnly = false;

    // Is sporadic printing allowed
    private boolean isZeroAvailable = true;

    private int fullIcon = R.drawable.star_full;
    private int halfIcon = R.drawable.star_half;
    private int emptyIcon = R.drawable.star_empty;

    private ArrayList<ImageView> list = new ArrayList<>();
    private int[] scoreMode;

    // Callback on slide
    private OnSlidingListener listener;

    public SlideToGradeLayout(Context context) {
        this(context, null);
    }

    public SlideToGradeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public SlideToGradeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
    }

    /**
     * Initialization method
     * Execute after setting the required parameters
     * This method needs to be executed at least once to display the view
     */
    public void init() {
        addIcon();
        initScoreMode();
        showScore(currentScore);
        isInit = true;
    }

    /**
     * Refresh the number of stars displayed
     *
     * @param score Star number
     */
    public void refresh(float score) {
        currentScore = score;
        showScore(currentScore);
    }

    /**
     * Add star
     */
    private void addIcon() {
        list.clear();
        removeAllViews();
        setOrientation(LinearLayout.HORIZONTAL);
        for (int i = 0; i < iconCount; i++) {
            ImageView imageView = new ImageView(context);
            LayoutParams layoutParams = new LayoutParams(dp2px(iconWidth), dp2px(iconHeight));
            if (i == 0) {
                layoutParams.setMargins(dp2px(iconPadding), 0, dp2px(iconPadding), 0);
            } else {
                layoutParams.setMargins(0, 0, dp2px(iconPadding), 0);
            }
            imageView.setImageDrawable(context.getResources().getDrawable(emptyIcon));
            addView(imageView, layoutParams);
            list.add(imageView);
        }
    }

    /**
     * Calculate the abscissa range of each star
     */
    private void initScoreMode() {
        scoreMode = new int[2 * iconCount];
        for (int i = 0; i < scoreMode.length; i++) {
            if (i % 2 == 0) {
                scoreMode[i] = dp2px(iconPadding + (i / 2) * (iconWidth + iconPadding));
            } else {
                scoreMode[i] = scoreMode[i - 1] + dp2px(iconPadding);
            }
        }
    }

    /**
     * Display the status of each star (sporadic, half star, one star)
     *
     * @param score Fraction
     */
    private void showScore(float score) {
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setImageDrawable(context.getResources().getDrawable(emptyIcon));
        }
        for (int i = 0; i < Math.floor(score); i++) {
            list.get(i).setImageDrawable(context.getResources().getDrawable(fullIcon));
        }
        if (score != Math.floor(score) && Math.floor(score) + 0.5 <= score) {
            list.get((int) Math.floor(score)).setImageDrawable(context.getResources().getDrawable(halfIcon));
        }
        if (listener != null && !isLock) {
            listener.onSliding(score);
        }
    }

    /**
     * Calculate the fraction according to the sliding x coordinate
     *
     * @param x Abscissa
     * @return Fraction
     */
    private float calculateScore(float x) {
        float score = 0;
        for (int i = 0; i < scoreMode.length; i++) {
            if (x > scoreMode[i]) {
                score += 0.5;
            } else {
                break;
            }
        }
        return !isZeroAvailable && score == 0 ? (isIntegerOnly ? 1 : 0.5f) : (isIntegerOnly ? Math.round(score) : score);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isLock) {
            return super.onTouchEvent(event);
        }
        if (currentScore != calculateScore(event.getX())) {
            currentScore = calculateScore(event.getX());
            showScore(currentScore);
        }
        return true;
    }

    private int dp2px(float dipValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    /**
     * Set star width
     *
     * @param iconWidth Width value in dp
     */
    public void setIconWidth(float iconWidth) {
        this.iconWidth = iconWidth;
    }

    /**
     * Set star height
     *
     * @param iconHeight Height value in dp
     */
    public void setIconHeight(float iconHeight) {
        this.iconHeight = iconHeight;
        refresh(currentScore);
    }

    /**
     * Set spacing
     *
     * @param iconPadding
     */
    public void setIconPadding(float iconPadding) {
        this.iconPadding = iconPadding;
        refresh(currentScore);
    }

    /**
     * Set whether sliding is locked
     * When set to true, only the score will be displayed, no response will be caused by sliding
     *
     * @param isLock Lock or not
     */
    public void setIsLock(boolean isLock) {
        this.isLock = isLock;
    }

    /**
     * Set one star icon
     *
     * @param fullIcon Icon resources
     */
    public void setFullIcon(@DrawableRes int fullIcon) {
        this.fullIcon = fullIcon;
    }

    /**
     * Set half Star Icon
     *
     * @param halfIcon Icon resources
     */
    public void setHalfIcon(@DrawableRes int halfIcon) {
        this.halfIcon = halfIcon;
    }

    /**
     * Set sporadic Icon
     *
     * @param emptyIcon Icon resources
     */
    public void setEmptyIcon(@DrawableRes int emptyIcon) {
        this.emptyIcon = emptyIcon;
    }

    /**
     * Set the number of stars
     *
     * @param iconCount Star number
     */
    public void setIconCount(int iconCount) {
        this.iconCount = iconCount;
    }

    /**
     * Set whether only integer stars are allowed
     * Half star when set to false
     *
     * @param isIntegerOnly
     */
    public void setIsIntegerOnly(boolean isIntegerOnly) {
        this.isIntegerOnly = isIntegerOnly;
    }

    /**
     * Set whether sporadic printing is allowed
     *
     * @param isZeroAvailable
     */
    public void setIsZeroAvailable(boolean isZeroAvailable) {
        this.isZeroAvailable = isZeroAvailable;
    }

    /**
     * Get current score
     *
     * @return Current score
     */
    public float getCurrentScore() {
        return currentScore;
    }

    /**
     * Get whether to initialize
     *
     * @return
     */
    public boolean isInit() {
        return isInit;
    }

    /**
     * Set sliding callback interface
     *
     * @param listener
     */
    public void setOnSlidingListener(OnSlidingListener listener) {
        this.listener = listener;
    }

    /**
     * Callback interface when sliding
     */
    public interface OnSlidingListener {
        /**
         * Callback on slide
         *
         * @param score Current score
         */
        void onSliding(float score);
    }
}

Three pictures:

star_full.png:

star_half.png :


star_empty.png:



Then it's ready to use.

MainActivity.java:

public class MainActivity extends Activity {
    private Button button;
    private SlideToGradeLayout gradeLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
    }

    private void initData() {

    }

    private void initView() {
        button = (Button) findViewById(R.id.button);
        gradeLayout = (SlideToGradeLayout) findViewById(R.id.gradeLayout);

        if (!gradeLayout.isInit()) {
            gradeLayout.init();
        }

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "YOU GRADE " + gradeLayout.getCurrentScore(), Toast.LENGTH_SHORT).show();
            }
        });
    }
}

main_activity.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <com.min.slidetogradelayout.SlideToGradeLayout
        android:id="@+id/gradeLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp" />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="10dp"
        android:text="GET SCORE" />

</LinearLayout>

The usage is very simple. The only thing to note is that the init() method needs to be initialized at least once before the view is displayed. Take a look at the effect:

                                                                 

Of course, the control also provides some settable parameters. The setting method has been written clearly in the source code annotation. It should be noted that the operation of these setting parameters must be in init() Method. Here are a few simple examples to add code:

if (!gradeLayout.isInit()) {
    gradeLayout.setIconCount(6);
    gradeLayout.setIconWidth(40);
    gradeLayout.setIconHeight(40);
    gradeLayout.setIsIntegerOnly(true);
    gradeLayout.setIsZeroAvailable(false);
    gradeLayout.init();
}
gradeLayout.refresh(5);
Run again:

                                                                 


Finally, the source address is attached: Click to open the link


This time we'll see you next time.

Tags: Android Java xml encoding

Posted on Thu, 30 Apr 2020 09:52:15 -0700 by fross