Android滚轮选择时间控件使用详解

滚轮选择控件

Android自带的选择时间控件有点丑,往往产品和设计都比较嫌弃,希望做成ios一样的滚轮选择,下面是我在NumberPicker的基础上自定义的选择控件,效果如下:

原理

  • 基于NumberPicker实现
  • 动态填充数值
  • 联动
  • 接口监听回调

实现滚轮效果有github上mark比较多的WheelView,但是阅读源码发现数据是一次性填入的,选择时间的话,填入10年就是10*365=3650条数据,也就是new出三千多个TextView,想想都觉得恐怖,肯定是不行的,于是便想到用NumberPicker,动态填充数据,一次只设置5个数据,当选中变化时,重新设置数据填充,所以关键在于填充的数据的计算。

设置数据部分逻辑代码:

  /**
   * 更新左侧控件
   * 日期:选择年控件
   * 时间:选择月份和日期控件
   *
   * @param timeMillis
   */
  private void updateLeftValue(long timeMillis) {
    SimpleDateFormat sdf;
    String str[] = new String[DATA_SIZE];
    if (mCurrentType == TYPE_PICK_DATE) {
      sdf = new SimpleDateFormat("yyyy");
      for (int i = 0; i < DATA_SIZE; i++) {
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(timeMillis);
        cal.add(Calendar.YEAR, (i - DATA_SIZE / 2));
        str[i] = sdf.format(cal.getTimeInMillis());
      }
    } else {
      sdf = new SimpleDateFormat("MM-dd EEE");
      for (int i = 0; i < DATA_SIZE; i++) {
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(timeMillis);
        cal.add(Calendar.DAY_OF_MONTH, (i - DATA_SIZE / 2));
        str[i] = sdf.format(cal.getTimeInMillis());
      }
    }
    mNpLeft.setDisplayedValues(str);
    mNpLeft.setValue(DATA_SIZE / 2);
    mNpLeft.postInvalidate();
  }

对滚轮的监听,并重新设置填充数据:

  @Override
  public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(mTimeMillis);
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    int hour = calendar.get(Calendar.HOUR_OF_DAY);
    int offset = newVal - oldVal;
    if (picker == mNpLeft) {
      if (mCurrentType == TYPE_PICK_DATE) {
        calendar.add(Calendar.YEAR, offset);
      } else {
        calendar.add(Calendar.DAY_OF_MONTH, offset);
      }
      updateLeftValue(calendar.getTimeInMillis());
      mTimeMillis = calendar.getTimeInMillis();
    } else if (picker == mNpMiddle) {
      if (mCurrentType == TYPE_PICK_DATE) {
        calendar.add(Calendar.MONTH, offset);
        if (calendar.get(Calendar.YEAR) != year) {
          calendar.set(Calendar.YEAR, year);
        }
      } else {
        calendar.add(Calendar.HOUR_OF_DAY, offset);
        if (calendar.get(Calendar.DAY_OF_MONTH) != day) {
          calendar.set(Calendar.DAY_OF_MONTH, day);
        }
        if (calendar.get(Calendar.MONTH) != month) {
          calendar.set(Calendar.MONTH, month);
        }
        if (calendar.get(Calendar.YEAR) != year) {
          calendar.set(Calendar.YEAR, year);
        }
      }
      updateMiddleValue(calendar.getTimeInMillis());
      updateRightValue(calendar.getTimeInMillis());
      mTimeMillis = calendar.getTimeInMillis();
    } else if (picker == mNpRight) {
      if (mCurrentType == TYPE_PICK_DATE) {
        int days = getMaxDayOfMonth(year, month + 1);
        if(day == 1 && offset < 0){
          calendar.set(Calendar.DAY_OF_MONTH,days);
        }else if(day == days && offset > 0){
          calendar.set(Calendar.DAY_OF_MONTH,1);
        }else{
          calendar.add(Calendar.DAY_OF_MONTH, offset);
        }

        if (calendar.get(Calendar.MONTH) != month) {
          calendar.set(Calendar.MONTH, month);
        }
        if (calendar.get(Calendar.YEAR) != year) {
          calendar.set(Calendar.YEAR, year);
        }
        Log.e(TAG,"time:::"+test.format(calendar.getTimeInMillis()));
      } else {
        calendar.add(Calendar.MINUTE, offset);
        if (calendar.get(Calendar.HOUR_OF_DAY) != hour) {
          calendar.set(Calendar.HOUR_OF_DAY, hour);
        }
        if (calendar.get(Calendar.DAY_OF_MONTH) != day) {
          calendar.set(Calendar.DAY_OF_MONTH, day);
        }
        if (calendar.get(Calendar.MONTH) != month) {
          calendar.set(Calendar.MONTH, month);
        }
        if (calendar.get(Calendar.YEAR) != year) {
          calendar.set(Calendar.YEAR, year);
        }
      }
      updateRightValue(calendar.getTimeInMillis());
      mTimeMillis = calendar.getTimeInMillis();
    }
    /**
     * 向外部发送当前选中时间
     */
    if (mOnSelectedChangeListener != null) {
      mOnSelectedChangeListener.onSelected(this,mTimeMillis);
    }
    Log.e(TAG, "selected time:" + test.format(mTimeMillis));
  }

选择数值和字符串

同样的,使用NumberPicker进行封装,动态填充数值从而实现滚动变换的效果。

  • 考虑到通用性,传入的是Object类型的数组,在控件里进行判断。
  • 可以选择一列数值、两列数值、三列数值,字符串同理。每一列数值可以设置它的单位、标题等,默认是隐藏,需要自己设置。
  • 可以设置步长step

完整代码如下:

package com.example.moore.picktimeview.widget;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.NumberPicker;
import android.widget.TextView;

/**
 * Created by Moore on 2016/10/21.
 */

public class PickValueView extends LinearLayout implements NumberPicker.OnValueChangeListener {
  private Context mContext;
  /**
   * 组件 标题、单位、滚轮
   */
  private TextView mTitleLeft, mTitleMiddle, mTitleRight;
  private TextView mUnitLeft, mUnitMiddle, mUnitRight;
  private MyNumberPicker mNpLeft, mNpMiddle, mNpRight;
  /**
   * 数据个数 1列 or 2列 or 3列
   */
  private int mViewCount = 1;
  /**
   * 一组数据长度
   */
  private final int DATA_SIZE = 3;

  /**
   * 需要设置的值与默认值
   */
  private Object[] mLeftValues;
  private Object[] mMiddleValues;
  private Object[] mRightValues;
  private Object mDefaultLeftValue;
  private Object mDefaultMiddleValue;
  private Object mDefaultRightValue;
  /**
   * 当前正在显示的值
   */
  private Object[] mShowingLeft = new Object[DATA_SIZE];
  private Object[] mShowingMiddle = new Object[DATA_SIZE];
  private Object[] mShowingRight = new Object[DATA_SIZE];

  /**
   * 步长
   */
  private int mLeftStep = 5;
  private int mMiddleStep = 1;
  private int mRightStep = 1;
  /**
   * 回调接口对象
   */
  private onSelectedChangeListener mSelectedChangeListener;

  public PickValueView(Context context) {
    super(context);
    this.mContext = context;
    generateView();
  }

  public PickValueView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.mContext = context;
    generateView();
  }

  public PickValueView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.mContext = context;
    generateView();
  }

  /**
   * 生成视图
   */
  private void generateView() {
    //标题
    LinearLayout titleLayout = new LinearLayout(mContext);
    LayoutParams titleParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    titleParams.setMargins(0, 0, 0, dip2px(12));
    titleLayout.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    titleLayout.setOrientation(HORIZONTAL);
    mTitleLeft = new TextView(mContext);
    mTitleMiddle = new TextView(mContext);
    mTitleRight = new TextView(mContext);

    LayoutParams params = new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);
    TextView[] titles = new TextView[]{mTitleLeft, mTitleMiddle, mTitleRight};
    for (int i = 0; i < titles.length; i++) {
      titles[i].setLayoutParams(params);
      titles[i].setGravity(Gravity.CENTER);
      titles[i].setTextColor(Color.parseColor("#3434EE"));
    }
    titleLayout.addView(mTitleLeft);
    titleLayout.addView(mTitleMiddle);
    titleLayout.addView(mTitleRight);
    //内容
    LinearLayout contentLayout = new LinearLayout(mContext);
    contentLayout.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    contentLayout.setOrientation(HORIZONTAL);
    contentLayout.setGravity(Gravity.CENTER);
    mNpLeft = new MyNumberPicker(mContext);
    mNpMiddle = new MyNumberPicker(mContext);
    mNpRight = new MyNumberPicker(mContext);
    mUnitLeft = new TextView(mContext);
    mUnitMiddle = new TextView(mContext);
    mUnitRight = new TextView(mContext);

    MyNumberPicker[] nps = new MyNumberPicker[]{mNpLeft, mNpMiddle, mNpRight};
    for (int i = 0; i < nps.length; i++) {
      nps[i].setLayoutParams(params);
      nps[i].setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
      nps[i].setOnValueChangedListener(this);
    }

    contentLayout.addView(mNpLeft);
    contentLayout.addView(mUnitLeft);
    contentLayout.addView(mNpMiddle);
    contentLayout.addView(mUnitMiddle);
    contentLayout.addView(mNpRight);
    contentLayout.addView(mUnitRight);

    this.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    this.setOrientation(VERTICAL);
    this.addView(titleLayout);
    this.addView(contentLayout);
  }

  /**
   * 初始化数据和值
   */
  private void initViewAndPicker() {
    if (mViewCount == 1) {
      this.mNpMiddle.setVisibility(GONE);
      this.mNpRight.setVisibility(GONE);
      this.mUnitMiddle.setVisibility(GONE);
      this.mUnitRight.setVisibility(GONE);
    } else if (mViewCount == 2) {
      this.mNpRight.setVisibility(GONE);
      this.mUnitRight.setVisibility(GONE);
    }

    //初始化数组值
    if (mLeftValues != null && mLeftValues.length != 0) {
      if (mLeftValues.length < DATA_SIZE) {
        for (int i = 0; i < mLeftValues.length; i++) {
          mShowingLeft[i] = mLeftValues[i];
        }
        for (int i = mLeftValues.length; i < DATA_SIZE; i++) {
          mShowingLeft[i] = -9999;
        }
      } else {
        for (int i = 0; i < DATA_SIZE; i++) {
          mShowingLeft[i] = mLeftValues[i];
        }
      }
      mNpLeft.setMinValue(0);
      mNpLeft.setMaxValue(DATA_SIZE - 1);
      if (mDefaultLeftValue != null)
        updateLeftView(mDefaultLeftValue);
      else
        updateLeftView(mShowingLeft[0]);
    }
    /**
     * 中间控件
     */
    if (mViewCount == 2 || mViewCount == 3) {
      if (mMiddleValues != null && mMiddleValues.length != 0) {
        if (mMiddleValues.length < DATA_SIZE) {
          for (int i = 0; i < mMiddleValues.length; i++) {
            mShowingMiddle[i] = mMiddleValues[i];
          }
          for (int i = mMiddleValues.length; i < DATA_SIZE; i++) {
            mShowingMiddle[i] = -9999;
          }
        } else {
          for (int i = 0; i < DATA_SIZE; i++) {
            mShowingMiddle[i] = mMiddleValues[i];
          }
        }
        mNpMiddle.setMinValue(0);
        mNpMiddle.setMaxValue(DATA_SIZE - 1);
        if (mDefaultMiddleValue != null)
          updateMiddleView(mDefaultMiddleValue);
        else
          updateMiddleView(mShowingMiddle[0]);
      }
    }

    /**
     * 右侧控件
     */
    if (mViewCount == 3) {
      if (mRightValues != null && mRightValues.length != 0) {
        if (mRightValues.length < DATA_SIZE) {
          for (int i = 0; i < mRightValues.length; i++) {
            mShowingRight[i] = mRightValues[i];
          }
          for (int i = mRightValues.length; i < DATA_SIZE; i++) {
            mShowingRight[i] = -9999;
          }
        } else {
          for (int i = 0; i < DATA_SIZE; i++) {
            mShowingRight[i] = mRightValues[i];
          }
        }
        mNpRight.setMinValue(0);
        mNpRight.setMaxValue(DATA_SIZE - 1);
        if (mDefaultRightValue != null)
          updateRightView(mDefaultRightValue);
        else
          updateRightView(mShowingRight[0]);
      }
    }

  }

  private void updateLeftView(Object value) {
    updateValue(value, 0);
  }

  private void updateMiddleView(Object value) {
    updateValue(value, 1);
  }

  private void updateRightView(Object value) {
    updateValue(value, 2);
  }

  /**
   * 更新滚轮视图
   *
   * @param value
   * @param index
   */
  private void updateValue(Object value, int index) {
    String showStr[] = new String[DATA_SIZE];
    MyNumberPicker picker;
    Object[] showingValue;
    Object[] values;
    int step;
    if (index == 0) {
      picker = mNpLeft;
      showingValue = mShowingLeft;
      values = mLeftValues;
      step = mLeftStep;
    } else if (index == 1) {
      picker = mNpMiddle;
      showingValue = mShowingMiddle;
      values = mMiddleValues;
      step = mMiddleStep;
    } else {
      picker = mNpRight;
      showingValue = mShowingRight;
      values = mRightValues;
      step = mRightStep;
    }

    if (values instanceof Integer[]) {
      for (int i = 0; i < DATA_SIZE; i++) {
        showingValue[i] = (int) value - step * (DATA_SIZE / 2 - i);
        int offset = (int) values[values.length - 1] - (int) values[0] + step;
        if ((int) showingValue[i] < (int) values[0]) {
          showingValue[i] = (int) showingValue[i] + offset;
        }
        if ((int) showingValue[i] > (int) values[values.length - 1]) {
          showingValue[i] = (int) showingValue[i] - offset;
        }
        showStr[i] = "" + showingValue[i];
      }
    } else {
      int strIndex = 0;
      for (int i = 0; i < values.length; i++) {
        if (values[i].equals(value)) {
          strIndex = i;
          break;
        }
      }
      for (int i = 0; i < DATA_SIZE; i++) {
        int temp = strIndex - (DATA_SIZE / 2 - i);
        if (temp < 0) {
          temp += values.length;
        }
        if (temp >= values.length) {
          temp -= values.length;
        }
        showingValue[i] = values[temp];
        showStr[i] = (String) values[temp];
      }
    }
    picker.setDisplayedValues(showStr);
    picker.setValue(DATA_SIZE / 2);
    picker.postInvalidate();
  }

  @Override
  public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    if (picker == mNpLeft) {
      updateLeftView(mShowingLeft[newVal]);
    } else if (picker == mNpMiddle) {
      updateMiddleView(mShowingMiddle[newVal]);
    } else if (picker == mNpRight) {
      updateRightView(mShowingRight[newVal]);
    }
    if (mSelectedChangeListener != null) {
      mSelectedChangeListener.onSelected(this, mShowingLeft[DATA_SIZE / 2], mShowingMiddle[DATA_SIZE / 2], mShowingRight[DATA_SIZE / 2]);
    }
  }

  /**
   * 设置数据--单列数据
   *
   * @param leftValues
   * @param mDefaultLeftValue
   */
  public void setValueData(Object[] leftValues, Object mDefaultLeftValue) {
    this.mViewCount = 1;
    this.mLeftValues = leftValues;
    this.mDefaultLeftValue = mDefaultLeftValue;

    initViewAndPicker();
  }

  /**
   * 设置数据--两列数据
   *
   * @param leftValues
   * @param mDefaultLeftValue
   * @param middleValues
   * @param defaultMiddleValue
   */
  public void setValueData(Object[] leftValues, Object mDefaultLeftValue, Object[] middleValues, Object defaultMiddleValue) {
    this.mViewCount = 2;
    this.mLeftValues = leftValues;
    this.mDefaultLeftValue = mDefaultLeftValue;

    this.mMiddleValues = middleValues;
    this.mDefaultMiddleValue = defaultMiddleValue;

    initViewAndPicker();
  }

  /**
   * 设置数据--三列数据
   *
   * @param leftValues
   * @param mDefaultLeftValue
   * @param middleValues
   * @param defaultMiddleValue
   * @param rightValues
   * @param defaultRightValue
   */
  public void setValueData(Object[] leftValues, Object mDefaultLeftValue, Object[] middleValues, Object defaultMiddleValue, Object[] rightValues, Object defaultRightValue) {
    this.mViewCount = 3;
    this.mLeftValues = leftValues;
    this.mDefaultLeftValue = mDefaultLeftValue;

    this.mMiddleValues = middleValues;
    this.mDefaultMiddleValue = defaultMiddleValue;

    this.mRightValues = rightValues;
    this.mDefaultRightValue = defaultRightValue;

    initViewAndPicker();
  }

  /**
   * 设置左边数据步长
   *
   * @param step
   */
  public void setLeftStep(int step) {
    this.mLeftStep = step;
    initViewAndPicker();
  }

  /**
   * 设置中间数据步长
   *
   * @param step
   */
  public void setMiddleStep(int step) {
    this.mMiddleStep = step;
    initViewAndPicker();
  }

  /**
   * 设置右边数据步长
   *
   * @param step
   */
  public void setRightStep(int step) {
    this.mRightStep = step;
    initViewAndPicker();
  }

  /**
   * 设置标题
   *
   * @param left
   * @param middle
   * @param right
   */
  public void setTitle(String left, String middle, String right) {
    if (left != null) {
      mTitleLeft.setVisibility(VISIBLE);
      mTitleLeft.setText(left);
    } else {
      mTitleLeft.setVisibility(GONE);
    }
    if (middle != null) {
      mTitleMiddle.setVisibility(VISIBLE);
      mTitleMiddle.setText(middle);
    } else {
      mTitleMiddle.setVisibility(GONE);
    }
    if (right != null) {
      mTitleRight.setVisibility(VISIBLE);
      mTitleRight.setText(right);
    } else {
      mTitleRight.setVisibility(GONE);
    }
    this.postInvalidate();
  }

  public void setUnitLeft(String unitLeft) {
    setUnit(unitLeft, 0);
  }

  public void setmUnitMiddle(String unitMiddle) {
    setUnit(unitMiddle, 1);
  }

  public void setUnitRight(String unitRight) {
    setUnit(unitRight, 2);
  }

  private void setUnit(String unit, int index) {
    TextView tvUnit;
    if (index == 0) {
      tvUnit = mUnitLeft;
    } else if (index == 1) {
      tvUnit = mUnitMiddle;
    } else {
      tvUnit = mUnitRight;
    }
    if (unit != null) {
      tvUnit.setText(unit);
    } else {
      tvUnit.setText(" ");
    }
    initViewAndPicker();
  }

  /**
   * 设置回调
   *
   * @param listener
   */
  public void setOnSelectedChangeListener(onSelectedChangeListener listener) {
    this.mSelectedChangeListener = listener;
  }

  /**
   * dp转px
   *
   * @param dp
   * @return
   */
  private int dip2px(int dp) {
    float scale = mContext.getResources().getDisplayMetrics().density;
    return (int) (scale * dp + 0.5f);
  }

  /**
   * 回调接口
   */
  public interface onSelectedChangeListener {
    void onSelected(PickValueView view, Object leftValue, Object middleValue, Object rightValue);
  }
}

关于NumberPicker

默认的NumberPicker往往字体颜色、分割线颜色等都是跟随系统,不能改变,考虑到可能比较丑或者有其他需求,所以自定义的NumberPicker,通过反射的方式更改里面的一些属性,代码如下:

package com.example.moore.picktimeview.widget;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.NumberPicker;

import java.lang.reflect.Field;

/**
 * Created by Moore on 2016/10/20.
 */

public class MyNumberPicker extends NumberPicker {
  private static int mTextSize = 16;
  private static int mTextColor = 0x000000;
  private static int mDividerColor = 0xFFFF00;

  public MyNumberPicker(Context context) {
    super(context);
    setNumberPickerDividerColor();
  }

  public MyNumberPicker(Context context, AttributeSet attrs) {
    super(context, attrs);
    setNumberPickerDividerColor();
  }

  public MyNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    setNumberPickerDividerColor();
  }

  @Override
  public void addView(View child) {
    super.addView(child);
    updateView(child);
  }

  @Override
  public void addView(View child, int index, ViewGroup.LayoutParams params) {
    super.addView(child, index, params);
    updateView(child);
  }

  @Override
  public void addView(View child, ViewGroup.LayoutParams params) {
    super.addView(child, params);
    updateView(child);
  }

  private void updateView(View view) {
    if (view instanceof EditText) {
//      ((EditText) view).setTextSize(mTextSize);
      ((EditText) view).setTextSize(17);
//      ((EditText) view).setTextColor(mTextColor);
      ((EditText) view).setTextColor(Color.parseColor("#6495ED"));
    }
  }

  private void setNumberPickerDividerColor() {
    Field[] pickerFields = NumberPicker.class.getDeclaredFields();
    /**
     * 设置分割线颜色
     */
    for (Field pf : pickerFields) {
      if (pf.getName().equals("mSelectionDivider")) {
        pf.setAccessible(true);
        try {
//          pf.set(this, new ColorDrawable(mDividerColor));
          pf.set(this, new ColorDrawable(Color.parseColor("#C4C4C4")));
        } catch (IllegalAccessException e) {
          e.printStackTrace();
        }
        break;
      }
    }
    /**
     * 设置分割线高度
     */
    for (Field pf : pickerFields) {
      if (pf.getName().equals("mSelectionDividerHeight")) {
        pf.setAccessible(true);
        try {
          pf.set(this, 2);
        } catch (IllegalAccessException e) {
          e.printStackTrace();
        }
        break;
      }
    }
    for (Field pf : pickerFields) {
      if (pf.getName().equals("mSelectorElementHeight")) {
        pf.setAccessible(true);
        try {
          pf.set(this, 2);
        } catch (IllegalAccessException e) {
          e.printStackTrace();
        }
        break;
      }
    }
  }

  public void setDividerColor(int color) {
    this.mDividerColor = color;
//    this.postInvalidate();
  }

  public void setTextColor(int color) {
    this.mTextColor = color;
//    this.postInvalidate();
  }

  public void setTextSize(int textSize) {
    this.mTextSize = textSize;
//    this.postInvalidate();
  }
}

完整Demo可前往github查看与下载,地址:https://github.com/lizebinbin/PickTimeView.git 谢谢!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android Spinner列表选择框的应用

    Android  Spinner列表选择框的应用 Spinner 是 Android 的列表选择框,不过 spinner 并不需要显示下拉列表,而是相当于弹出一个菜单供用户选择. Spinner 属性: ● android:spinnerMode:列表显示的模式,有两个选择,为弹出列表(dialog)以及下拉列表(dropdown),如果不特别设置,为下拉列表. ● android:entries:使用<string-array.../>资源配置数据源. ● android:prompt:对当

  • Android自定义View实现多图片选择控件

    前言 相信很多朋友在开发中都会遇到图片上传的情况,尤其是多图上传,最经典的莫过于微信的图片选择了.所有很多情况下会使用到多图选择,所以就有了这篇文章,今天抽点时间写了个控件.  •支持自定义选择图片的样式  •支持设置图片选择数量  •支持图片预览,删除  •支持图片拍照 先来看看效果 实现分析 假如不定义控件,我们要实现这样一个功能,无非是写个GridView在item点击的时候去显示图片进行选择,在返回界面的时候进行GridView的数据刷新.我们把这些逻辑写在我们自定义的GridView中

  • Android 列表选择框 Spinner详解及实例

    Android 列表选择框 Spinner详解及实例 Spinner 是 Android 的列表选择框,不过 spinner 并不需要显示下拉列表,而是相当于弹出一个菜单供用户选择. Spinner 属性: ● android:spinnerMode:列表显示的模式,有两个选择,为弹出列表(dialog)以及下拉列表(dropdown),如果不特别设置,为下拉列表. ● android:entries:使用<string-array.../>资源配置数据源. ● android:prompt:

  • 可支持快速搜索筛选的Android自定义选择控件

    Android 自定义支持快速搜索筛选的选择控件使用方法,具体如下 项目中遇到选择控件选项过多,需要快速查找匹配的情况. 做了简单的Demo,效果图如下: 源码地址:https://github.com/whieenz/SearchSelect 这个控件是由Dialog+SearchView+ListView实现的.Dialog用来承载选择控件,SearchView实现输入,ListView展示结果.设计概要图如下: 一.自定义Dialog Dialog布局文件 <?xml version=&quo

  • Android组件实现列表选择框功能

    android提供的列表选择框(Spinner)相当于web端用户注册时的选择下拉框,比如注册候选择省份城市等.如下图便是一个列表选择框 下拉列表的列表选择项能够通过xml文件的android:entries属性指定,或是在java代码中导入,属性android:prompt是列表项的标题. 一    列表项数据: 实际运用当中,很多下拉列表项的数据实际是可知的,可以放在xml资源文件中.这时,开发者可以通过xml属性进行指定数据. 除了资源文件之外,开发者还能够使用适配器适配数据源.(适配器:

  • Android控件BottomSheet实现底边弹出选择列表

    底边弹出一个选择列表这是一个比较常用的选择条件或跳转的很好的方法,可以很好的隐藏各个选项.在需要使用时在底边弹出.而BottomSheet就是这样的一个控件. 使用 1.导入build compile 'com.cocosw:bottomsheet:1.3.0' 2.在res/values/colors.xml文件中添加以下代码: <!--首页item文字颜色--> <color name="colorSubtitle">#999</color> &

  • Android编程实现变化的双重选择框功能示例

    本文实例讲述了Android编程实现变化的双重选择框功能.分享给大家供大家参考,具体如下: 原理:定义四个RadioGroup,通过第一个RadioGroup的选择来控制其余几个radiogroup的显隐 布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&quo

  • Android高仿IOS 滚轮选择控件

    最近根据项目需要,整理了一个相对比较全面的 WheelView 使用控件,借用之前看到的一句话来说,就是站在巨人肩膀上,进行了一些小调整. 这里先贴上效果图 一般常用的时间选择格式,,单项选择,以及城市联动,这里基本都可以满足了. 这里把 单项选择,和 日期时间选择 给提出到 Util 类中,代码如下: public class Util { /** * 时间选择回调 */ public interface TimerPickerCallBack { void onTimeSelect(Stri

  • Android列表选择框Spinner使用方法详解

    安卓提供的列表选择框(Spinner)相当于web端用户注册时的选择下拉框,比如注册候选择省份城市等.如下图便是一个列表选择框 下拉列表的列表选择项能够通过xml文件的android:entries属性指定,或是在java代码中导入,属性android:prompt是列表项的标题. 一 列表项数据 实际运用当中,很多下拉列表项的数据实际是可知的,可以放在xml资源文件中.这时,开发者可以通过xml属性进行指定数据. 除了资源文件之外,开发者还能够使用适配器适配数据源.(适配器:如果您的电脑不能接

  • Android 实现IOS 滚轮选择控件的实例(源码下载)

     Android 实现IOS 滚轮选择控件的实例 最近根据项目需要,整理了一个相对比较全面的 WheelView 使用控件,借用之前看到的一句话来说,就是站在巨人肩膀上,进行了一些小调整. 这里先贴上效果图 一般常用的时间选择格式,,单项选择,以及城市联动,这里基本都可以满足了. 这里把 单项选择,和 日期时间选择 给提出到 Util 类中,代码如下: public class Util { /** * 时间选择回调 */ public interface TimerPickerCallBack

  • Android开发实现布局中为控件添加选择器的方法

    本文实例讲述了Android开发实现布局中为控件添加选择器的方法.分享给大家供大家参考,具体如下: 在开发过程中,动态交互的一些展示效果可以通过布局中添加选择器实现,这样就可减少Activity等的代码数量,MVP开发中降低耦合性,使开发人员在写代码时只需要关注逻辑处理. 比如:一个按钮,原本背景图片为红色,字体为黑色,点击时候背景图片为黄色,字体改为白色. 这类简单效果在布局时就可以实现: <Button android:id="@+id/btn_start" android:

随机推荐