Android自定义的进度条NumberProgressBar实例分享

 更新时间:2016年9月20日 19:56  点击:1988
进度条可以显示加载的进度,为手机应用提升了不少用户体验,本文我们来分享两个在Android应用开发中自定义的进度条实例。

先看看效果吧:

01.gif


这是个自定义的view,我这里就不去深究了,看demo界面的代码,看看如何使用的。

MainActivity.java

/** 
 * demo主界面 
 * @author jan 
 */  
public class MainActivity extends Activity implements OnProgressBarListener {  
      
    private Timer timer;  
    //进度条组件  
    private NumberProgressBar bnp;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        bnp = (NumberProgressBar) findViewById(R.id.numberbar1);  
        bnp.setOnProgressBarListener(this);  
        //创建一个定时任务,模拟进度更新  
        timer = new Timer();  
        timer.schedule(new TimerTask() {  
            @Override  
            public void run() {  
                runOnUiThread(new Runnable() {  
                    @Override  
                    public void run() {  
                        //递增progress数值  
                        bnp.incrementProgressBy(5);  
                    }  
                });  
            }  
        }, 1000, 100);  
    }  
  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        //最后别忘了取消定时任务  
        timer.cancel();  
    }  
    /** 
     * 实现对进度实时监听,max默认为100 
     */  
    @Override  
    public void onProgressChange(int current, int max) {  
        if (current == max) {  
            Toast.makeText(getApplicationContext(), getString(R.string.finish),  
                    Toast.LENGTH_SHORT).show();  
            bnp.setProgress(0);  
        }  
    }  
}


2.在我们的xml布局文件中加入他,别忘了加定义命名空间:xmlns:custom="http://schemas.android.com/apk/res-auto":

<com.daimajia.numberprogressbar.NumberProgressBar  
        android:id="@+id/numberbar1"  
        style="@style/NumberProgressBar_Default"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:padding="20dp"  
        custom:progress_current="0" />


3.他的style设定有以下属性:

<style name="NumberProgressBar_Default">  
        <item name="android:layout_height">wrap_content</item>  
        <item name="android:layout_width">match_parent</item>  
  
        <item name="progress_max">100</item>  
        <item name="progress_current">0</item>  
  
        <item name="progress_unreached_color">#CCCCCC</item>  
        <item name="progress_reached_color">#3498DB</item>  
  
        <item name="progress_text_size">14sp</item>  
        <item name="progress_text_color">#3498DB</item>  
  
        <item name="progress_reached_bar_height">6.5dp</item>  
        <item name="progress_unreached_bar_height">5.75dp</item>  
    </style>


4.再来看看他的源代码吧。

public class NumberProgressBar extends View {  
  
    private int mMaxProgress = 100;  
  
    /** 
     * Current progress, can not exceed the max progress. 
     */  
    private int mCurrentProgress = 0;  
  
    /** 
     * The progress area bar color. 
     */  
    private int mReachedBarColor;  
  
    /** 
     * The bar unreached area color. 
     */  
    private int mUnreachedBarColor;  
  
    /** 
     * The progress text color. 
     */  
    private int mTextColor;  
  
    /** 
     * The progress text size. 
     */  
    private float mTextSize;  
  
    /** 
     * The height of the reached area. 
     */  
    private float mReachedBarHeight;  
  
    /** 
     * The height of the unreached area. 
     */  
    private float mUnreachedBarHeight;  
  
    /** 
     * The suffix of the number. 
     */  
    private String mSuffix = "%";  
  
    /** 
     * The prefix. 
     */  
    private String mPrefix = "";  
  
  
    private final int default_text_color = Color.rgb(66, 145, 241);  
    private final int default_reached_color = Color.rgb(66, 145, 241);  
    private final int default_unreached_color = Color.rgb(204, 204, 204);  
    private final float default_progress_text_offset;  
    private final float default_text_size;  
    private final float default_reached_bar_height;  
    private final float default_unreached_bar_height;  
  
    /** 
     * For save and restore instance of progressbar. 
     */  
    private static final String INSTANCE_STATE = "saved_instance";  
    private static final String INSTANCE_TEXT_COLOR = "text_color";  
    private static final String INSTANCE_TEXT_SIZE = "text_size";  
    private static final String INSTANCE_REACHED_BAR_HEIGHT = "reached_bar_height";  
    private static final String INSTANCE_REACHED_BAR_COLOR = "reached_bar_color";  
    private static final String INSTANCE_UNREACHED_BAR_HEIGHT = "unreached_bar_height";  
    private static final String INSTANCE_UNREACHED_BAR_COLOR = "unreached_bar_color";  
    private static final String INSTANCE_MAX = "max";  
    private static final String INSTANCE_PROGRESS = "progress";  
    private static final String INSTANCE_SUFFIX = "suffix";  
    private static final String INSTANCE_PREFIX = "prefix";  
    private static final String INSTANCE_TEXT_VISIBILITY = "text_visibility";  
  
    private static final int PROGRESS_TEXT_VISIBLE = 0;  
  
  
    /** 
     * The width of the text that to be drawn. 
     */  
    private float mDrawTextWidth;  
  
    /** 
     * The drawn text start. 
     */  
    private float mDrawTextStart;  
  
    /** 
     * The drawn text end. 
     */  
    private float mDrawTextEnd;  
  
    /** 
     * The text that to be drawn in onDraw(). 
     */  
    private String mCurrentDrawText;  
  
    /** 
     * The Paint of the reached area. 
     */  
    private Paint mReachedBarPaint;  
    /** 
     * The Paint of the unreached area. 
     */  
    private Paint mUnreachedBarPaint;  
    /** 
     * The Paint of the progress text. 
     */  
    private Paint mTextPaint;  
  
    /** 
     * Unreached bar area to draw rect. 
     */  
    private RectF mUnreachedRectF = new RectF(0, 0, 0, 0);  
    /** 
     * Reached bar area rect. 
     */  
    private RectF mReachedRectF = new RectF(0, 0, 0, 0);  
  
    /** 
     * The progress text offset. 
     */  
    private float mOffset;  
  
    /** 
     * Determine if need to draw unreached area. 
     */  
    private boolean mDrawUnreachedBar = true;  
  
    private boolean mDrawReachedBar = true;  
  
    private boolean mIfDrawText = true;  
  
    /** 
     * Listener 
     */  
    private OnProgressBarListener mListener;  
  
    public enum ProgressTextVisibility {  
        Visible, Invisible  
    }  
  
    public NumberProgressBar(Context context) {  
        this(context, null);  
    }  
  
    public NumberProgressBar(Context context, AttributeSet attrs) {  
        this(context, attrs, R.attr.numberProgressBarStyle);  
    }  
  
    public NumberProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {  
        super(context, attrs, defStyleAttr);  
  
        default_reached_bar_height = dp2px(1.5f);  
        default_unreached_bar_height = dp2px(1.0f);  
        default_text_size = sp2px(10);  
        default_progress_text_offset = dp2px(3.0f);  
  
        //load styled attributes.  
        final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar,  
                defStyleAttr, 0);  
  
        mReachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_reached_color, default_reached_color);  
        mUnreachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_unreached_color, default_unreached_color);  
        mTextColor = attributes.getColor(R.styleable.NumberProgressBar_progress_text_color, default_text_color);  
        mTextSize = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_size, default_text_size);  
  
        mReachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_reached_bar_height, default_reached_bar_height);  
        mUnreachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_unreached_bar_height, default_unreached_bar_height);  
        mOffset = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_offset, default_progress_text_offset);  
  
        int textVisible = attributes.getInt(R.styleable.NumberProgressBar_progress_text_visibility, PROGRESS_TEXT_VISIBLE);  
        if (textVisible != PROGRESS_TEXT_VISIBLE) {  
            mIfDrawText = false;  
        }  
  
        setProgress(attributes.getInt(R.styleable.NumberProgressBar_progress_current, 0));  
        setMax(attributes.getInt(R.styleable.NumberProgressBar_progress_max, 100));  
  
        attributes.recycle();  
        initializePainters();  
    }  
  
    @Override  
    protected int getSuggestedMinimumWidth() {  
        return (int) mTextSize;  
    }  
  
    @Override  
    protected int getSuggestedMinimumHeight() {  
        return Math.max((int) mTextSize, Math.max((int) mReachedBarHeight, (int) mUnreachedBarHeight));  
    }  
  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));  
    }  
  
    private int measure(int measureSpec, boolean isWidth) {  
        int result;  
        int mode = MeasureSpec.getMode(measureSpec);  
        int size = MeasureSpec.getSize(measureSpec);  
        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();  
        if (mode == MeasureSpec.EXACTLY) {  
            result = size;  
        } else {  
            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();  
            result += padding;  
            if (mode == MeasureSpec.AT_MOST) {  
                if (isWidth) {  
                    result = Math.max(result, size);  
                } else {  
                    result = Math.min(result, size);  
                }  
            }  
        }  
        return result;  
    }  
  
    @Override  
    protected void onDraw(Canvas canvas) {  
        if (mIfDrawText) {  
            calculateDrawRectF();  
        } else {  
            calculateDrawRectFWithoutProgressText();  
        }  
  
        if (mDrawReachedBar) {  
            canvas.drawRect(mReachedRectF, mReachedBarPaint);  
        }  
  
        if (mDrawUnreachedBar) {  
            canvas.drawRect(mUnreachedRectF, mUnreachedBarPaint);  
        }  
  
        if (mIfDrawText)  
            canvas.drawText(mCurrentDrawText, mDrawTextStart, mDrawTextEnd, mTextPaint);  
    }  
  
    private void initializePainters() {  
        mReachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mReachedBarPaint.setColor(mReachedBarColor);  
  
        mUnreachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mUnreachedBarPaint.setColor(mUnreachedBarColor);  
  
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mTextPaint.setColor(mTextColor);  
        mTextPaint.setTextSize(mTextSize);  
    }  
  
  
    private void calculateDrawRectFWithoutProgressText() {  
        mReachedRectF.left = getPaddingLeft();  
        mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;  
        mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() + getPaddingLeft();  
        mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;  
  
        mUnreachedRectF.left = mReachedRectF.right;  
        mUnreachedRectF.right = getWidth() - getPaddingRight();  
        mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f;  
        mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;  
    }  
  
    private void calculateDrawRectF() {  
  
        mCurrentDrawText = String.format("%d", getProgress() * 100 / getMax());  
        mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;  
        mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);  
  
        if (getProgress() == 0) {  
            mDrawReachedBar = false;  
            mDrawTextStart = getPaddingLeft();  
        } else {  
            mDrawReachedBar = true;  
            mReachedRectF.left = getPaddingLeft();  
            mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;  
            mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() - mOffset + getPaddingLeft();  
            mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;  
            mDrawTextStart = (mReachedRectF.right + mOffset);  
        }  
  
        mDrawTextEnd = (int) ((getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f));  
  
        if ((mDrawTextStart + mDrawTextWidth) >= getWidth() - getPaddingRight()) {  
            mDrawTextStart = getWidth() - getPaddingRight() - mDrawTextWidth;  
            mReachedRectF.right = mDrawTextStart - mOffset;  
        }  
  
        float unreachedBarStart = mDrawTextStart + mDrawTextWidth + mOffset;  
        if (unreachedBarStart >= getWidth() - getPaddingRight()) {  
            mDrawUnreachedBar = false;  
        } else {  
            mDrawUnreachedBar = true;  
            mUnreachedRectF.left = unreachedBarStart;  
            mUnreachedRectF.right = getWidth() - getPaddingRight();  
            mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f;  
            mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;  
        }  
    }  
  
    /** 
     * Get progress text color. 
     * 
     * @return progress text color. 
     */  
    public int getTextColor() {  
        return mTextColor;  
    }  
  
    /** 
     * Get progress text size. 
     * 
     * @return progress text size. 
     */  
    public float getProgressTextSize() {  
        return mTextSize;  
    }  
  
    public int getUnreachedBarColor() {  
        return mUnreachedBarColor;  
    }  
  
    public int getReachedBarColor() {  
        return mReachedBarColor;  
    }  
  
    public int getProgress() {  
        return mCurrentProgress;  
    }  
  
    public int getMax() {  
        return mMaxProgress;  
    }  
  
    public float getReachedBarHeight() {  
        return mReachedBarHeight;  
    }  
  
    public float getUnreachedBarHeight() {  
        return mUnreachedBarHeight;  
    }  
  
    public void setProgressTextSize(float textSize) {  
        this.mTextSize = textSize;  
        mTextPaint.setTextSize(mTextSize);  
        invalidate();  
    }  
  
    public void setProgressTextColor(int textColor) {  
        this.mTextColor = textColor;  
        mTextPaint.setColor(mTextColor);  
        invalidate();  
    }  
  
    public void setUnreachedBarColor(int barColor) {  
        this.mUnreachedBarColor = barColor;  
        mUnreachedBarPaint.setColor(mReachedBarColor);  
        invalidate();  
    }  
  
    public void setReachedBarColor(int progressColor) {  
        this.mReachedBarColor = progressColor;  
        mReachedBarPaint.setColor(mReachedBarColor);  
        invalidate();  
    }  
  
    public void setReachedBarHeight(float height) {  
        mReachedBarHeight = height;  
    }  
  
    public void setUnreachedBarHeight(float height) {  
        mUnreachedBarHeight = height;  
    }  
  
    public void setMax(int maxProgress) {  
        if (maxProgress > 0) {  
            this.mMaxProgress = maxProgress;  
            invalidate();  
        }  
    }  
  
    public void setSuffix(String suffix) {  
        if (suffix == null) {  
            mSuffix = "";  
        } else {  
            mSuffix = suffix;  
        }  
    }  
  
    public String getSuffix() {  
        return mSuffix;  
    }  
  
    public void setPrefix(String prefix) {  
        if (prefix == null)  
            mPrefix = "";  
        else {  
            mPrefix = prefix;  
        }  
    }  
  
    public String getPrefix() {  
        return mPrefix;  
    }  
  
    public void incrementProgressBy(int by) {  
        if (by > 0) {  
            setProgress(getProgress() + by);  
        }  
  
        if(mListener != null){  
            mListener.onProgressChange(getProgress(), getMax());  
        }  
    }  
  
    public void setProgress(int progress) {  
        if (progress <= getMax() && progress >= 0) {  
            this.mCurrentProgress = progress;  
            invalidate();  
        }  
    }  
  
    @Override  
    protected Parcelable onSaveInstanceState() {  
        final Bundle bundle = new Bundle();  
        bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());  
        bundle.putInt(INSTANCE_TEXT_COLOR, getTextColor());  
        bundle.putFloat(INSTANCE_TEXT_SIZE, getProgressTextSize());  
        bundle.putFloat(INSTANCE_REACHED_BAR_HEIGHT, getReachedBarHeight());  
        bundle.putFloat(INSTANCE_UNREACHED_BAR_HEIGHT, getUnreachedBarHeight());  
        bundle.putInt(INSTANCE_REACHED_BAR_COLOR, getReachedBarColor());  
        bundle.putInt(INSTANCE_UNREACHED_BAR_COLOR, getUnreachedBarColor());  
        bundle.putInt(INSTANCE_MAX, getMax());  
        bundle.putInt(INSTANCE_PROGRESS, getProgress());  
        bundle.putString(INSTANCE_SUFFIX, getSuffix());  
        bundle.putString(INSTANCE_PREFIX, getPrefix());  
        bundle.putBoolean(INSTANCE_TEXT_VISIBILITY, getProgressTextVisibility());  
        return bundle;  
    }  
  
    @Override  
    protected void onRestoreInstanceState(Parcelable state) {  
        if (state instanceof Bundle) {  
            final Bundle bundle = (Bundle) state;  
            mTextColor = bundle.getInt(INSTANCE_TEXT_COLOR);  
            mTextSize = bundle.getFloat(INSTANCE_TEXT_SIZE);  
            mReachedBarHeight = bundle.getFloat(INSTANCE_REACHED_BAR_HEIGHT);  
            mUnreachedBarHeight = bundle.getFloat(INSTANCE_UNREACHED_BAR_HEIGHT);  
            mReachedBarColor = bundle.getInt(INSTANCE_REACHED_BAR_COLOR);  
            mUnreachedBarColor = bundle.getInt(INSTANCE_UNREACHED_BAR_COLOR);  
            initializePainters();  
            setMax(bundle.getInt(INSTANCE_MAX));  
            setProgress(bundle.getInt(INSTANCE_PROGRESS));  
            setPrefix(bundle.getString(INSTANCE_PREFIX));  
            setSuffix(bundle.getString(INSTANCE_SUFFIX));  
            setProgressTextVisibility(bundle.getBoolean(INSTANCE_TEXT_VISIBILITY) ? Visible : Invisible);  
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));  
            return;  
        }  
        super.onRestoreInstanceState(state);  
    }  
  
    public float dp2px(float dp) {  
        final float scale = getResources().getDisplayMetrics().density;  
        return dp * scale + 0.5f;  
    }  
  
    public float sp2px(float sp) {  
        final float scale = getResources().getDisplayMetrics().scaledDensity;  
        return sp * scale;  
    }  
  
    public void setProgressTextVisibility(ProgressTextVisibility visibility) {  
        mIfDrawText = visibility == Visible;  
        invalidate();  
    }  
  
    public boolean getProgressTextVisibility() {  
        return mIfDrawText;  
    }  
  
    public void setOnProgressBarListener(OnProgressBarListener listener){  
        mListener = listener;  
    }  
}


其实还是很简单的,没啥好说的了,有兴趣的可以下代码看看。




ndroid UI设计与开发 自定义进度条

国际惯例,先看效果图吧

01.jpeg


实现步骤一:先定义进度条的风格样式

<!-- 自定义进度条 -->
<style name="ProgressBar_Mini" parent="@android:style/Widget.ProgressBar.Horizontal">
    <item name="android:maxHeight">50dip</item>
    <item name="android:minHeight">8dip</item>
    <item name="android:indeterminateOnly">false</item>
    <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
    <item name="android:progressDrawable">@drawable/progressbar_mini</item>
</style>


实现步骤二:定义样式style中用到的@drawable/progressbar_mini资源文件。可以再xml中绘制进度条的不同样式(圆角,直角……)和颜色。

<?xml version="1.0" encoding="utf-8"?>  
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >  
    <item android:id="@android:id/background">    
         <shape >    
                <corners android:radius="5dip" />    
                <gradient    
                    android:angle="270"    
                    android:centerY="0.75"    
                    android:endColor="#F5F5F5"    
                    android:startColor="#BEBEBE" />    
            </shape>    
    </item>    
    
    <item android:id="@android:id/secondaryProgress">    
    
        <clip >    
            <shape >    
                <corners android:radius="0dip" />    
                <gradient    
                    android:angle="270"    
                    android:centerY="0.75"    
                   android:endColor="#165CBC"    
                    android:startColor="#85B0E9" />    
            </shape>    
        </clip>    
    </item>    
    
    <item android:id="@android:id/progress">    
    
        <clip >    
            <shape >    
                <corners android:radius="5dip" />    
                <gradient    
                    android:angle="270"    
                    android:centerY="0.75"    
                   android:endColor="#165CBC"    
                    android:startColor="#85B0E9" />    
            </shape>    
              
        </clip>    
    </item>    
</layer-list>



实现步骤三:写java文件,定义进度条上显示的文字。

public class MyProgressBar extends ProgressBar {
    private String text_progress;
    private Paint mPaint;//画笔
    public MyProgressBar(Context context) {
        super(context);
        initPaint();
    }
    public MyProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }
    public MyProgressBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initPaint();
    }
    
    @Override
    public synchronized void setProgress(int progress) {
        super.setProgress(progress);
        setTextProgress(progress);
    }
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        Rect rect=new Rect();
        this.mPaint.getTextBounds(this.text_progress, 0, this.text_progress.length(), rect);
        int x = (getWidth() / 2) - rect.centerX();
        int y = (getHeight() / 2) - rect.centerY();
        canvas.drawText(this.text_progress, x, y, this.mPaint);
    }
    /**
     * 
     *description: 初始化画笔
     *Create by lll on 2013-8-13 下午1:41:49
     */
    private void initPaint(){
        this.mPaint=new Paint();
        this.mPaint.setAntiAlias(true);
        this.mPaint.setColor(Color.WHITE);
    }
    private void setTextProgress(int progress){ 
        int i = (int) ((progress * 1.0f / this.getMax()) * 100);
        this.text_progress = String.valueOf(i) + "%";
    }
}


最后一步:xml文件中引用自定义的进度条:

 <com.example.widgetshop.MyProgressBar
    android:id="@+id/progressBar1"
    style="@style/ProgressBar_Mini"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:progress="100"
    android:layout_marginTop="24dp" />


OK,一个简单的更改背景和样式其带文字显示的进度条就完成。

ListView 与 ScrollView 同在一界面会导致ListView 显示变形,ListView只显示出了一个条目的高度,本文我们来谈谈如何解决Android应用中ListView 和 ScrollView 共生的问题。

ListView 与 ScrollView 同在一界面会导致ListView 显示变形,ListView只显示出了一个条目的高度,本文我们来谈谈如何解决Android应用中ListView 和 ScrollView 共生的问题。


一开始就想着用一个ScrollView把主要内容和评论区的ListView包起来,然后添加各个控件的内容即可,但是写出来之后发现ListView只显示出了一个条目的高度,并且不能滑动,网上搜了一下发现原因是ScrollView和ListView都是可滑动的,把它们放在一块会有冲突,最后还是ScrollView获得了焦点,ListView不能滑动。网上的解决方法最多的是在加载ListView时用getMeasure计算每个条目和分割线的高度,然后相加,把结果设置为ListView控件的高度,不过貌似是只适用于ListView每个条目高度都一样的情况(没有试过,很奇怪为什么会这样)。要么就是自定义一个继承自ListView的控件,也是事先设置好ListView的高度,但这样总归比较麻烦,而且准确度不如由系统自己构造好。

懒癌发作实在不想自己去做这些事情,于是便想试一下比较投机的方法,就是在ListView的Adapter的getView方法中根据position构造不同的界面,即如果position是0,则用原来主要信息的xml文件取inflate convertView,否则就用评论条目的xml去inflate,经试验果然可行。之后不死心想看下有没有更好的实现方法,去overflow上找了一下,发现有人推荐的方法和我的差不多,所以认为这种方法是比较好的,不需要做额外的工作,只需要把inflate的工作由主Activity放在Adapter里就可以了。


getView方法


@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    /*主信息界面*/
    if(0 == position)
    {
        MainHolder holder = null;
        convertView = inflater.inflate(R.layout.info, parent, false);
        holder = new MainHolder();
        convertView.setTag(holder);
                ······
               &nbsp;······
    }
    /*评论界面*/
    else
    {
        ItemHolder holder = null;
        convertView = inflater.inflate(R.layout.item, parent, false);
        holder = new ItemHolder();
        convertView.setTag(holder);
        ······
        ······
        return convertView;
    }
}




另一个方法解决

Android 解决ListView 和 ScrollView 共存冲突的问题

ListView 与 ScrollView 同在一界面会导致ListView 显示变形,因为ListView 也有自带的滚动事件,故无法与ScrollView 相容,可能造成的现象是ListView 只能显示一行或者两行,其他数据在那一点儿宽的地方做滚动,甚不雅观。

下面是我的一个实现 步骤:

    1、继承LinearLayout,既然会冲突那就不用ListView 改成线性布局做动态布局效果
    2、继承BaseAdapter ,可以参照一下Android app源码中 Widget 目录下的SimpleAdapter 为前面扩展的LinearLayout做数据。
    3、模拟数据填充扩展后的BaseAdapter 为扩展后的LinearLayout 加载数据

第一步:新建LinearLayoutForListView 类使其扩展LinearLayout重写以下两个方法:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public LinearLayoutForListView(Context context) {
    super(context);
}
public LinearLayoutForListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
}




这两个方法可选,不过建议都写上,第一个方法可以让我们通过 编程的方式 实例化出来,第二个方法可以允许我们通过 XML的方式注册 控件,可以在第二个方法里面为扩展的复合组件加属性。

为其添加get / set 方法

/**
     * 获取Adapter
     * 
     * @return adapter
     */
    public AdapterForLinearLayout getAdpater() {
        return adapter;
    }
    /**
     * 设置数据
     * 
     * @param adpater
     */
    public void setAdapter(AdapterForLinearLayout adpater) {
        this.adapter = adpater;
        bindLinearLayout();
    }
    /**
     * 获取点击事件
     * 
     * @return
     */
    public OnClickListener getOnclickListner() {
        return onClickListener;
    }
    /**
     * 设置点击事件
     * 
     * @param onClickListener
     */
    public void setOnclickLinstener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }



 第二步:新建AdapterForLinearLayout 类继承自BaseAdapter,并为其添加构造函数

 

private LayoutInflater mInflater;
    private int resource;
    private List<? extends Map<String, ?>> data;
    private String[] from;
    private int[] to;
    public AdapterForLinearLayout(Context context,
            List<? extends Map<String, ?>> data, int resouce, String[] from,
            int[] to) {
        this.data = data;
        this.resource = resouce;
        this.data = data;
        this.from = from;
        this.to = to;
        this.mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }


 

此构造函数模仿 simpleAdapter 通过传进来的resouce 为布局设置数据。通过继承BaseAdapter 重要的实现方法在下面getView ,此方法判断通过传进来的 String[] from 与 int[] to 为分别查找出View 并为View 设置相应的Text,代码如下:

 

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        convertView = mInflater.inflate(resource, null);
        Map<String, ?> item = data.get(position);
        int count = to.length;
        for (int i = 0; i < count; i++) {
            View v = convertView.findViewById(to[i]);
            bindView(v, item, from[i]);
        }
        convertView.setTag(position);
        return convertView;
    }
    /**
     * 绑定视图
     * @param view
     * @param item
     * @param from
     */
    private void bindView(View view, Map<String, ?> item, String from) {
        Object data = item.get(from);
        if (view instanceof TextView) {
            ((TextView) view).setText(data == null ? "" : data.toString());
        }
    }



 Tip:

BindView 方法是一个自定义方法,在方法体内可以为通过判断使本类更具灵活性,如上,你不仅可以判断是TextView 并且可以传入任何你想要的View 只要在方法体内加入相应判断即可,数据可以通过data 做相应处理,具体如何操作读者可另行测试。

    convertView.setTag(position); 此句代码为View 设置tag 在以后我们可以通过 getTag 找出下标,后文有介绍如何通过下标操作数据。

下面是两个类的全部代码,读者可以无须更改直接使用:

LinearLayoutForListView
package com.terry.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
public class LinearLayoutForListView extends LinearLayout {
    private AdapterForLinearLayout adapter;
    private OnClickListener onClickListener = null;
    /**
     * 绑定布局
     */
    public void bindLinearLayout() {
        int count = adapter.getCount();
        for (int i = 0; i < count; i++) {
            View v = adapter.getView(i, null, null);
            v.setOnClickListener(this.onClickListener);
            if (i == count - 1) {
                LinearLayout ly = (LinearLayout) v;
                ly.removeViewAt(2);
            }
            addView(v, i);
        }
        Log.v("countTAG", "" + count);
    }
    public LinearLayoutForListView(Context context) {
        super(context);
    }
    public LinearLayoutForListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
    /**
     * 获取Adapter
     * 
     * @return adapter
     */
    public AdapterForLinearLayout getAdpater() {
        return adapter;
    }
    /**
     * 设置数据
     * 
     * @param adpater
     */
    public void setAdapter(AdapterForLinearLayout adpater) {
        this.adapter = adpater;
        bindLinearLayout();
    }
    /**
     * 获取点击事件
     * 
     * @return
     */
    public OnClickListener getOnclickListner() {
        return onClickListener;
    }
    /**
     * 设置点击事件
     * 
     * @param onClickListener
     */
    public void setOnclickLinstener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}
AdapterForLinearLayout
package com.terry.widget;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class AdapterForLinearLayout extends BaseAdapter {
    private LayoutInflater mInflater;
    private int resource;
    private List<? extends Map<String, ?>> data;
    private String[] from;
    private int[] to;
    public AdapterForLinearLayout(Context context,
            List<? extends Map<String, ?>> data, int resouce, String[] from,
            int[] to) {
        this.data = data;
        this.resource = resouce;
        this.data = data;
        this.from = from;
        this.to = to;
        this.mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return data.size();
    }
    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return data.get(position);
    }
    @SuppressWarnings("unchecked")
    public String get(int position, Object key) {
        Map<String, ?> map = (Map<String, ?>) getItem(position);
        return map.get(key).toString();
    }
    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        convertView = mInflater.inflate(resource, null);
        Map<String, ?> item = data.get(position);
        int count = to.length;
        for (int i = 0; i < count; i++) {
            View v = convertView.findViewById(to[i]);
            bindView(v, item, from[i]);
        }
        convertView.setTag(position);
        return convertView;
    }
    /**
     * 绑定视图
     * @param view
     * @param item
     * @param from
     */
    private void bindView(View view, Map<String, ?> item, String from) {
        Object data = item.get(from);
        if (view instanceof TextView) {
            ((TextView) view).setText(data == null ? "" : data.toString());
        }
    }
}


对应的XML 如下:


<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:id="@+id/TextView01"
        android:layout_marginLeft="10px" android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_width="wrap_content" android:layout_height="wrap_content">
    </TextView>
    <TextView android:id="@+id/TextView02" android:layout_width="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:layout_marginLeft="10px" android:layout_height="wrap_content">
    </TextView>
    <View android:layout_height="1px" android:background="#FFFFFF"
        android:layout_width="fill_parent"></View>
</LinearLayout>


 

第三步:主页面使用控件并为其设置数据

XML如下:

<com.terry.widget.LinearLayoutForListView
                    android:orientation="vertical" android:layout_width="450px"
                    android:layout_height="fill_parent" android:id="@+id/ListView01">
                </com.terry.widget.LinearLayoutForListView>



加载数据如下:
    

lv = (LinearLayoutForListView) findViewById(R.id.ListView01);
    for (int i = 0; i < 10; i++) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("key_name", "name" + i);
        map.put("value_name", "value" + i);
        list.add(map);
    }
    final AdapterForLinearLayout Layoutadpater = new AdapterForLinearLayout(
            this, list, R.layout.test, new String[] { "key_name",
                    "value_name" }, new int[] { R.id.TextView01,
                    R.id.TextView02 });



     
事件操作,并通过下标得到数据源:
    

lv.setOnclickLinstener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            Toast.makeText(
                    BlueToothActivity.this,
                    Layoutadpater.get(Integer.parseInt(v.getTag()
                            .toString()), "key_name"), 1000).show();
        }
    });
    lv.setAdapter(Layoutadpater);

    

Tip:get方法是在Layoutadpater 封装的一个通过下标获取相应数据的方法请参考上文。

至此完成。有碰到这个问题的朋友可以试试。

本文我们总结了三种Android应用向用户发送提示信息的方法,分别是1)发送Toast信息;2)弹出对话框;3)发送通知。

Android应用向用户发送提示信息的三种方法是:

1)发送Toast信息

2)弹出对话框

3)发送通知


方式1:发送Toast信息:

这种方式最简单,在之前的学习中多次使用过。Toast是在一个浮动于应用之上的View中显示信息,显示一定的时间间隔后自动消失,不可获得焦点。

最简单的用法就是之前的学习中一直使用的:通过一个静态的makeText()方法返回一个Toast对象,然后调用show()方法。

如:

布局文件添加一个Button:

<Button

        android:id="@+id/button1"

        android:onClick="showToast"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_gravity="center_horizontal"

        android:layout_margin="20dp"

        android:text="@string/show_toast" />

MainActivity.java添加一个showToast()方法:

public void showToast(View view){

          Toast.makeText(this, "客观,您要的吐司来啦", Toast.LENGTH_LONG).show();

}

显示结果:

 


通常情况下,Toast.makeText()这个方法已然够用了,但是,如果有些别的需求,就需要看一下Toast的帮助信息了:

 


在Toast这个类中定义了两个常量:LENGTH_LONG和LENGTH_SHORT,用来表示Toast显示的时长。想让Toast停留的时间长一点,可以设置为LENGTH_LONG,否则设置为LENGTH_SHORT。

LENGTH_LONG的实际值为1,LENGTH_SHORT的实际值为0。在用到的地方也可以直接传入0或者1,但是阅读起来就显得不太直观了。

 


Toast中只定义了一个构造函数,传入一个上下文对象,返回一个空的Toast对象。

更常用的是,通过下面两个静态方法获取Toast对象:

 


这两个方法的区别在于第二个参数,该参数表示要显示的文本内容,第一个方法传入strings.xml中的一个字符串id,而第二个方法直接传入字符串对象,之前用到的都是第二个方法。下面测试下传入id的方法:

strings.xml中加入一个<string>节点:

<string name="tv_text">客官,这个是放在strings.xml的Toast,请慢用</string>

修改showToast()方法:

public void showToast(View view){

          Toast.makeText(this, R.string.tv_text, Toast.LENGTH_LONG).show();

}

运行结果:

 


如果直接使用Toast的构造方法获取一个Toast对象的话,该对象是一个空的Toast,这时,是不能直接show()的,毕竟没有任何要显示的内容。

如果:new Toast(this).show();

会报错:

 


提示,必须先调用setView():

 


当然,肯定有对应的getView():

 


这两个方法,分别用来给Toast设置要显示的View和取得当前Toast对象的View。

测试下setView()方法:

新建一个tv.xml布局文件:

<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" >

    <TextView

        android:id="@+id/tv"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/tv_text" />

 
    <Button

        android:id="@+id/btn1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Button" />

</LinearLayout>

修改showToast()方法:

public void showToast(View view){

          Toast toast = new Toast(this);

          View v = getLayoutInflater().from(this).inflate(R.layout.tv, null);

          toast.setView(v);

          toast.show();

}

显示效果:


 

直接把tv.xml中的内容都显示出来了,真丑……虽然显示有按钮,即便设置了事件监听,但是由于Toast是不能获取焦点的,所以点击是没有任何反应的。

在使用makeText()方法时,传入了三个参数,而构造方法只传入了与之对应的第一个表示上下文的参数,因而肯定有对应的setter方法:

 


setText()用于设置Toast中显示的信息,但是,注意到帮助文档中的信息:是用于修改之前使用makeText()创建的Toast对象显示的信息,如Toast对象是通过构造方法创建的是不能调用setText()方法的。

 


setDuration()用于设置显示的时长。

感觉还是挺鸡肋的,加起来都不如一个makeText()。

下面这个方法,可能看起来稍微有用些:

 



用来设置Toast的显示位置,如:

修改tv.xml设置个绿色背景,不要按钮了,太丑也没用:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:background="#00ff00"

    android:orientation="vertical" >

    <TextView

        android:id="@+id/tv"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="客官,您要的Toast来啦,请慢用" />

</LinearLayout>

修改showToast()方法:

public void showToast(View view){

          Toast toast = new Toast(this);

          toast.setView(getLayoutInflater().inflate(R.layout.tv, null));

          toast.setDuration(1);

          toast.setGravity(Gravity.CENTER, 0, 0);

          toast.show();

}

Gravity.CENTER表示的水平垂直均居中显示,后面的参数分别表示在第一个参数的基础上x轴方向上的偏移及y轴方向上的偏移,正数表示x轴上向右移动,y轴上向下移动;负数则表示x轴上向左移动,y轴上向上移动。这里指定为0,表示不偏移。

显示效果:

 

确实,居中显示了,而且是绿色背景,还是好丑,实际中应该不会这样用吧,费力不讨好。

总结:上面一大堆无用的东西,实际使用中直接使用makeText()方法获取Toast对象,需要修改显示位置,则调用setGravity()方法即可。实在需要自定义布局时再使用构造及setView()方法。

方式2:弹出对话框:

通过弹出对话框的形式向用户发送提示信息的方式,就需要用到AlertDialog这个类,准确来说,用的更多的是它的内部类AlertDialog.Builder类。

其实,弹出对话框的流程很简单:

1)获取一个AlertDialog.Builder对象:

AlertDialog.Builder builder = new Builder(this);

 

2)调用Builder的各种setXX方法,设置对话框的样式:

如:

builder.setTitle("俺是对话框");

builder.setMessage("没什么要说的");

3)显示对话框:

builder.show();

 

1.弹出一个简单对话框的示例:

修改布局文件,添加一个按钮,用于弹出对话框:

<Button

        android:id="@+id/button2"

        android:onClick="showDialog"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_gravity="center_horizontal"

        android:layout_margin="20dp"

        android:text="弹出对话框" />

在MainActivity中添加一个button2回调的方法:

public void showDialog(View view){

          AlertDialog.Builder builder = new Builder(this);

          builder.setTitle("俺是对话框");

          builder.setMessage("没什么要说的");

          builder.setPositiveButton("确定", new OnClickListener() {

                

                 @Override

                 public void onClick(DialogInterface dialog, int which) {

                      Toast.makeText(MainActivity.this, "你点了确定,啥也没有", 1).show();

                 }

           });

          builder.setNegativeButton("取消", new OnClickListener() {

                

                 @Override

                 public void onClick(DialogInterface dialog, int which) {

                      Toast.makeText(MainActivity.this, "取消啦", 1).show();

                 }

           });

          builder.show();

}

运行结果:

 


但是,此时如果按下Back键,该对话框就自动关闭了,一般我们需要的是用户或者按下确定或者按下取消,这时就可以设置按下Back不进行任何操作:

builder.setCancelable(false);

 

2.弹出一个单选框,修改showDialog()方法:

builder.setTitle("明天做什么");

final String[] items = new String[]{"看书","看电影","散步"};

builder.setSingleChoiceItems(items, -1, new OnClickListener() {

            

             @Override

             public void onClick(DialogInterface dialog, int which) {

                   // TODO Auto-generated method stub

                   Toast.makeText(MainActivity.this, "你选了"+items[which], 1).show();

                   dialog.dismiss();

             }

});

第一个参数是一个数组类型的单选项

第二个参数表示默认选定项目的下标,-1表示默认一个都不选

第三个参数指定选定项目的事件监听器。

dialog.dismiss();表示关闭对话框

显示效果:

 


3.弹出一个复选框,修改showDialog()方法:

builder.setTitle("哪个电影好看点");

      final String[] items = new String[]{"道士下山","大圣归来","战狼2","捉妖记","栀子花开"};

      builder.setMultiChoiceItems(items, new boolean[]{false,false,false,false,false}, new OnMultiChoiceClickListener() {

            

             @Override

             public void onClick(DialogInterface dialog, int which, boolean isChecked) {

                   // TODO Auto-generated method stub

                   Toast.makeText(MainActivity.this, "你觉得"+items[which]+(isChecked?"好看":"不好看"), 0).show();

             }

});

运行结果:

 


4.显示一个进度对话框:

ProgressDialog pd = new ProgressDialog(this);

pd.setTitle("正在缓冲……");

pd.setMessage("这网好烂啊……");

pd.show();

运行结果:

 


调用: pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 可以将进度对话框改为水平进度条的样式:


final ProgressDialog pd = new ProgressDialog(this);

      pd.setTitle("正在缓冲……");

      pd.setMessage("这网好烂啊……");

      pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

      pd.setMax(100);

      pd.show();

      new Thread(){

            public void run(){

                 try {

                        for (int i = 0; i < 100; i++) {

                              Thread.sleep(1000);

                              pd.setProgress(i);

                        }

                        pd.dismiss();

                   } catch (Exception e) {

                        e.printStackTrace();

                   }

            }

      }.start();

运行结果:

 


注意:此时点击屏幕对话框之外的部分,发现对话框消失了,解决方法:调用pd.setCancelable(false)。

方式3:发送通知:

当应用程序不在前台时,或者只有后台服务在运行,也许通过通知的方式向用户发送提示信息更方便一些:

要使用通知,涉及到的类有:NotificationManager和Notification这两个类。

见名知意,前一个用于管理通知,后一个就是被管理的通知实体类。

首先看管理器类:

NotificationManager要管理通知,所以有几个比较常用的方法:

首先要获取NotificationManager对象:

但是,查看帮助手册却没有发现它的构造方法……

文档中这样描述的:

 


所以,应当通过getSystemService(String)方法获取NotificationManager对象。

这个方法时Context中的方法,查看帮助手册发现:

 


Context中定义了好几个String类型的常量,其中就有NOTIFICATION_SERVICE,后面的描述信息更是直接说明通过将这个字符串常量传给getSystemService()可以获取NotificationManager实例。

 


但是getSystemService()方法返回的是Object对象,故而,需要强制类型转换:

NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

 


notify()方法用于发出一个通知,cancel()方法由于取消通知。

有了通知管理器,要让它发送通知,还是需要有通知实体的:

 


构造方法参数说明:

第一个参数用来指定通知在状态栏的图标

第二个参数用来指定通知在状态栏的描述

第三个参数用来指定什么时候显示

如:

Notification notification = new Notification(R.drawable.ic_launcher, "这是一个通知", System.currentTimeMillis());

 


Notification中的方法就这几个,最有用的只有setLatestEventInfo()方法

 


用于给通知在任务栏中的显示设置一个标准的布局

参数说明:

第一个参数指定上下文

第二个参数指定在任务栏中显示的标题

第三个参数指定在任务栏中显示的内容

第四个参数是PendingIntent类型的,是一种特殊的意图,用于指定通知被点击之后的动作,若设置为空则表示不进行任何处理。

一个简单的通知示例:

在布局文件中添加第三个按钮,用于发送通知:

<Button

        android:id="@+id/button3"

        android:onClick="showNotification"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_gravity="center_horizontal"

        android:layout_margin="20dp"

        android:text="弹出通知" />

在MainActivity中添加一个showNotification()方法,用于按钮的回调:

public void showNotification(View view){

          NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

          Notification notification = new Notification(R.drawable.ic_launcher, "这是一个通知", System.currentTimeMillis());

          notification.setLatestEventInfo(this, "这是个通知", "没有通知内容", null);

          manager.notify(123, notification);

}

运行效果:

 


查看任务栏:

 


但是,点击这个通知是没有任何反应的。

因为notification.setLatestEventInfo(this, "这是个通知", "没有通知内容", null);第四个参数设置为null了。

下面稍微了解下关于第四个参数的内容:

PendingIntent:延迟意图,是一种特殊的意图,这种意图并不是在发出之后立即得到执行,而是交由其他应用在恰当的时候进行执行处理。

PendingIntent实例的获得可以通过以下几个静态方法:

getActivity(Context, int, Intent, int)返回一个PendingIntent实例用于启动一个新的Activity。

getBroadcast(Context, int, Intent, int) 返回一个PendingIntent实例用于发出广播

getService(Context, int, Intent, int) 返回一个PendingIntent实例用于启动一个新的Service。

它们均需要传入一个上下文对象,及一个普通的Intent对象。

下面获取一个PendingIntent对象,用来打开一个新的Activity,然后将其传入notification.setLatestEventInfo()方法:

新建一个Actvity:

public class NotificationActivity extends Activity {

 

      @Override

      protected void onCreate(Bundle savedInstanceState) {

           super.onCreate(savedInstanceState);

           setContentView(R.layout.tv);

      }

}

布局文件:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:background="#00ff00"

    android:orientation="vertical" >

    <TextView

        android:id="@+id/tv"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="客官,您要的通知来啦,请细阅" />

</LinearLayout>


修改showNotification()方法:

public void showNotification(View view){

          NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

          Notification notification = new Notification(R.drawable.ic_launcher, "这是一个通知", System.currentTimeMillis());

          Intent intent = new Intent(this, NotificationActivity.class);

          PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

          notification.setLatestEventInfo(this, "这是个通知", "没有通知内容", pi);

          manager.notify(123, notification);

}

运行结果:

点击通知:

 


Notification中更多的是设置通知的各个属性,来更改通知的类型:

 


这些字段都是共有的,可以直接赋值:

如:notificaion.icon设置通知的图标

notification.sound设置通知的铃音

notification.vibrate设置通知的震动模式,需要赋值为一个long的数组

如notification.vibrate = new long[]{0,1000,500,1000,500};下标为偶数的表示静止时间,下标为奇数的表示震动时间,单位为毫秒

ledARGB、ledOffMS、ledOnMS可以用来通知到来时led灯的显示方式

这些都可以自己测试使用下,这里不一一列举了。

此外,新版本中可以采用与对话框类似的Builder内部类方式,创建Notification对象那个,并设置其属性,但出于兼容性的考虑,这里没有列举:

简单示例如下:

Notification notification = new Notification.Builder(this)

          .setSmallIcon(R.drawable.ic_launcher).setContentTitle("这是个通知")

          .setContentText("没有通知内容").getNotification();

          manager.notify(123, notification);

以上,就是三种向用户发送提示信息的三种方式。

本教程我们来讲讲在android开发经常用到的简单控件,android自定义控件简单示例,ListView控件的简单使用。

基本控件


1.TextView:

功能与传统的桌面应用开发中的Label控件相似,用于显示文本信息

如:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#0000ff"
        android:textSize="40sp"
        android:text="@string/hello_world" />


显示效果:


上面的xml代码中,设置了几个常用的属性:

android:layout_width和android:layout_height分别设置控件的宽高

textColor设置显示的文本的颜色

textSize设置显示的文本的字体大小

text设置显示的文本内容。

2.Button:

前面用到的比较多,经常被用到的就是通过id获取按钮,然后绑定单击监听事件,这里仅列举个例子:

activity_main.xml:

<TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#0000ff"
        android:textSize="40sp"
        android:text="@string/hello_world" />
    <Button
        android:id="@+id/btn"
        android:layout_below="@id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btnText"/>


MainActivity.java:

public class MainActivity extends ActionBarActivity {
 
    private TextView tv;
      private Button btn;
 
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        btn = (Button) findViewById(R.id.btn);
        tv = (TextView) findViewById(R.id.tv);
        btn.setOnClickListener(new OnClickListener() {
                
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      tv.setText("It's changed!");
                 }
           });
    }
}


3.EditText:

即文本输入框,如下修改程序,在按钮之上添加一个EditText,点击按钮,会获取EditText的值并把它设置为TextView的Text属性:

activity_main.xml:

<TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#0000ff"
        android:textSize="40sp"
        android:text="@string/hello_world" />
    <EditText
        android:id="@+id/et"
        android:layout_below="@id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="@string/hintText"
        />
      <Button
        android:id="@+id/btn"
        android:layout_below="@id/et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btnText"/>


MainActivity.java:

public class MainActivity extends ActionBarActivity {
    private TextView tv;
      private Button btn;
      private EditText et;
 
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        btn = (Button) findViewById(R.id.btn);
        tv = (TextView) findViewById(R.id.tv);
        et = (EditText)findViewById(R.id.et);
        btn.setOnClickListener(new OnClickListener() {
                
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      Editable text = et.getText();
                      tv.setText(text.toString());
                 }
           });
    }
}


运行效果:

 
输入值,然后点击按钮:

 


注意到由于EditText的layout_height属性是wrap_content,所以会随着输入内容的增多不断变大,影响整体布局。若想固定其高度,可以设置maxLines属性,设置最多只显示的行数,其他内容向上滚动

如:android:maxLines = “1”

EditText的高度就不会变化了。

4.ImageView:

使用来显示图片的一个控件,之前的程序中曾经用到过,当然,它最主要的属性肯定是要显示图片的来源了,即android:src属性,将要显示的图片存放在res/drawable中,如图片名为hero.png。要显示该图片,设置android:src=”@drawable/hero”即可。

<ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/hero"/>


显示结果:



5.ProgressBar:

即进度条,使用style属性,可以设置不同的显示风格:

1)不设置style属性或者设置为style="?android:attr/progressBarStyle" ,环形显示

 
2)style="?android:attr/progressBarStyleHorizontal",水平横条显示

 


3)style="?android:attr/progressBarStyleLarge",大号的环形显示

 


4)style="?android:attr/progressBarStyleSmall",小号的

 


进度条当然是用来显示进度的,通过findViewById()获取ProgressBar,然后使用setProgress()就可以设置当前进度,使用getProgress()可以获取当前进度。

如:

布局代码:

<ProgressBar
          android:id="@+id/pb"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          style="?android:attr/progressBarStyleHorizontal"
          android:max="100"
          />
      <Button
          android:id="@+id/btn"
          android:layout_below="@id/pb"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/add_progress"/>


Activity代码:

public class MainActivity extends ActionBarActivity {
    private ProgressBar pb;
      private Button btn;
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pb = (ProgressBar) findViewById(R.id.pb);
        btn = (Button) findViewById(R.id.btn);
        Log.i("PB",pb.getProgress()+"");
        btn.setOnClickListener(new OnClickListener() {
                
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      Log.i("PB",pb.getProgress()+"");
                      pb.setProgress(pb.getProgress()+10);
                 }
           });
    }
}


运行结果:

 


初始时,默认进度为0



多次点击按钮之后:

 


达到android:max所设置的最大值后,再加也不会有变化了。

6.AlertDialog:

这个控件就是弹出一个对话框,类似于桌面开发中的模态对话框,必须关闭该对话框,才能进行后续交互操作,可用于显示比较重要的内容。

AlertDialog的构造方法都是protected,没法直接通过构造来创建AlertDialog,但是可以通过其内部类Builder来创建。

具体使用可以参考帮助手册中关于这个内部类的帮助信息,下面举个简单例子:

AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        dialog.setTitle("Warning");
        dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                      // TODO Auto-generated method stub
                     
                 }
           });
        dialog.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
                
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                      // TODO Auto-generated method stub
                     
                 }
           });
        dialog.setMessage("warning, hahaha");
        dialog.show();


运行结果:

 


7.ProgressDialog:

类似于AlertDialog,也是对话框,不过它显示的内容是一个进度条,好像是对话框和进度条两个控件的结合。

ProgressDialog pd = new ProgressDialog(this);

pd.setTitle("Data Loading...");

pd.show();

运行结果:




自定义控件简单示例

有时候,可能觉得系统提供的控件太丑,就会需要自定义控件来实现自己想要的效果。

以下主要参考《第一行代码》

1.自定义一个标题栏:

系统自带的标题栏很丑,且没什么大的作用,所以我们之前会在onCreate()中调用requestWindowFeature(Window.FEATURE_NO_TITLE);设置不显示标题栏。

下面自定义一个标题栏,中间显示标题,左右各有一个按钮:

title.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="wrap_content"
    android:orientation="horizontal"
    android:background="#bbbbbb" >
    <Button
        android:id="@+id/btn_back"
        android:text="@string/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:layout_gravity="left|center_vertical"
        android:textColor="#0099cc"
        android:layout_weight="1"/>
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#0099cc"
        android:text="@string/this_is_title"
        android:layout_gravity="center"
        android:gravity="center"
        android:layout_weight="2"/>
    <Button
        android:id="@+id/btn_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/edit"
        android:layout_margin="5dp"
        android:layout_gravity="right|center_vertical"
        android:textColor="#0099cc"
        android:layout_weight="1"/>
</LinearLayout>


Activity代码:

protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);     
           requestWindowFeature(Window.FEATURE_NO_TITLE);
           setContentView(R.layout.title);
}

运行结果:

 


2.复用布局代码:

想让这个标题栏应用在以后的每个布局文件,要怎么做呢?

总不能每次都把这些xml代码重写一遍吧。

android布局中提供了类似于c预处理指令#include的<include>标签,可以实现布局代码的复用。

下面新建一个first_layout.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:orientation="vertical" >
    <include layout="@layout/title"/>
    <Button android:id="@+id/btn"
        android:text="@string/i_m_a_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>


修改setContentView(R.layout.first_layout);

显示结果:

 


现在Back和Edit按钮都没有任何事件处理的,怎样实现点击Back按钮就结束当前Activity呢?方法跟之前的做法完全一样,使用findViewById()根据id找到Back按钮,然后设置click事件监听即可。

代码如下:

public class FirstActivity extends Activity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);     
           requestWindowFeature(Window.FEATURE_NO_TITLE);
           setContentView(R.layout.first_layout);
           Button btn = (Button) findViewById(R.id.btn_back);
           btn.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      FirstActivity.this.finish();
                 }
           });
      }
}


布局文件的复用已然通过<include>实现了,但是每次都要重新写事件监听,还是觉得麻烦……到这里一般就会想到抽象出一个自定义类,每次需要的时候,直接使用该自定义类不就行了,其实就是自定义控件的做法了。

3.自定义控件,复用功能代码

TitleLinearLayout.java代码:

public class TitleLinearLayout extends LinearLayout {
      public TitleLinearLayout(Context context, AttributeSet attrs) {
           super(context, attrs);
           LayoutInflater.from(context).inflate(R.layout.title, this);
           Button btn_back = (Button) findViewById(R.id.btn_back);
           btn_back.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      Log.i("clicked","back");
                      ((Activity)getContext()).finish();
                 }
           });
      }
}


继承自LinearLayout,实现带两个参数的构造方法。在构造方法中,加载布局文件,并对其中的Back按钮进行事件监听设置。

LayoutInflater.from(context).inflate(R.layout.title, this);用于动态加载布局文件。

注意到,Activity中有一个获取LayoutInflater的方法,所以,也可以使用下面一行代码加载布局文件:

((Activity)context).getLayoutInflater().inflate(R.layout.title, this);这种方法,在Activity代码中比较常用,而这里需要进行类型强转,反倒麻烦点,而且不如第一个方法安全。

如何使用自定义的控件呢?

first_layout代码如下:

<?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:orientation="vertical" >
    <cn.csc.custom_ui.TitleLinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </cn.csc.custom_ui.TitleLinearLayout>
    <Button android:id="@+id/btn"
        android:text="@string/i_m_a_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>


说明:

1)在布局文件中,引用自定义的控件,需要使用完整的类限定名,即包名.类名的方式;

2)在定义控件中,设置属性时,使用alt+/进行代码提示补全功能将经常不可用,标签名可以先设置为内置控件,然后进行属性的设置,之后再把标签名改回到自定义的控件的完整限定名即可。



控件ListView的简单使用

MVC模式:

MVC的基本原理就是通过Controller连接View和Model。当View中所显示的数据发生变化时,会通知Controller,然后由Controller调用Model中的相关方法执行相应的数据修改操作。反之,当Model中的数据发生变化时,也会通知Controller,由Controller通知View更新显示内容。如此一来,就使得数据部分与视图部分相分离,任何一方发生改变都不会影响到另一方。

而在android中,MVC的一个常见应用就是ListView显示数据。V代表的就是显示控件;M代表的是各种数据源,可以是自己定义的List或者数组,也可以是数据库,文件等;C代表的是Adapter类,android中比较常见的adapter有:BaseAdapter,ArrayAdapter,SimpleAdapter等。

ListView通过setAdapter方法实现了其和一个Adapter对象的绑定,Adapter一般通过getView()方法返回当前列表项所要显示的View对象,完成了对Model中数据的读取。

当Model发生变化时,会调用BaseAdapter.notifyDataSetChanged()方法通知组件数据已然变化,此时Adapter就会调用getView()方法重新显示组件内容。

ListView:间接继承自抽象类AdapterView。

常见方法:

void  setOnItemClickListener(AdapterView.OnItemClickListener listener)

void  setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener)

void  setAdapter(ListAdapter adapter)

Adapter:是一个接口

主要的方法:

abstract int  getCount()  返回要显示的item总数

abstract Object  getItem(int position)  根据item索引返回该item

abstract long  getItemId(int position)  返回item的id。

abstract View  getView(int position, View convertView, ViewGroup parent)返回一个用来展示数据源中索引为position的View对象。

BaseAdapter是一个间接实现了Adapter接口的抽象类

自定义Adapter继承BaseAdapter时,需要提供上面四个方法的实现。主要要实现的是getCount()和geView()方法。

ArrayAdapter<T>, CursorAdapter, SimpleAdapter则直接继承自BaseAdapter,实现了其抽象方法。

ListView示例1:展示一个字符串数组中的各个字符串。

main_layout.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:orientation="vertical" >
    <ListView
        android:id="@+id/listview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>


MainActivity.java:

public class MainActivity extends Activity {
     
      private String[] strs;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
           // TODO Auto-generated method stub
           super.onCreate(savedInstanceState);
           setContentView(R.layout.main_layout);
           strs = new String[]{"aaa","bbb","ccc","ddd","eee","fff","ggg","hhh","iii"};
           ListView lv = (ListView) findViewById(R.id.listview);
           lv.setAdapter(new MyBaseAdapter());
          
      }
      class MyBaseAdapter extends BaseAdapter{
           @Override
           public int getCount() {
                 return strs.length;
           }
           @Override
           public Object getItem(int position) {
                 // TODO Auto-generated method stub
                 return null;
           }
           @Override
           public long getItemId(int position) {
                 return 0;
           }
           @Override
           public View getView(int position, View convertView, ViewGroup parent) {
                 TextView tv = null;
                 tv = new TextView(MainActivity.this);
                 Log.i("listview", position+"get view");
                 tv.setText(strs[position]);
                 tv.setTextSize(40);
                 tv.setTextColor(Color.RED);
                 return tv;
           }
      }
}

运行结果:

 


注意到,每次滚动屏幕显示新的item,或将滚出屏幕上方的item重新显示出来都会调用getItem()方法。而上面的代码中每次都会新建一个TextView,这样做是存在问题的。

getView(int position, View convertView, ViewGroup parent),其中参数convertView在允许的情况下,会保存旧的View实例,以便拿来复用。所以,可以利用该参数来优化上面的getView()实现。

代码修改如下:

public View getView(int position, View convertView, ViewGroup parent) {
 
                 TextView tv = null;
                 Log.i("listview", position+"get view");
                 if(convertView == null){
                      tv = new TextView(MainActivity.this);
                 }
                 else{
                      tv = (TextView) convertView;
                 }
                 tv.setText(strs[position]);
                 tv.setTextSize(40);
                 tv.setTextColor(Color.RED);
                 return tv;
}


其实,若只是显示数组中的内容,直接使用ArrayAdapter会比较方便:

MainActivity.java修改如下:

private String[] strs;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
       // TODO Auto-generated method stub
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main_layout);
       strs = new String[]{"aaa","bbb","ccc","ddd","eee","fff","ggg","hhh","iii"};
       ListView lv = (ListView) findViewById(R.id.listview);
       ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strs);
       lv.setAdapter(adapter);
      
  }


运行结果:

 


其中android.R.layout.simple_list_item_1是系统自带的一个布局id。

一般来说,在ListView中显示的东西可能更加复杂点,只靠一个TextView肯定解决不了,这时,就可以为ListView中的item根据需要编写一个布局文件。

如:item_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/hero"
        />
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="35sp"
        />
</LinearLayout>


用于在线性布局中水平显示一个图片和一个字符串。

修改MyAdapter内部类的getView():

public View getView(int position, View convertView, ViewGroup parent) {
 
         View view = null;
         Log.i("listview", position+"get view");
         if(convertView == null){
              view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, null);
         }
         else{
              view =  convertView;
         }
         TextView tv = (TextView) view.findViewById(R.id.tv_name);
         tv.setText(strs[position]);
         return view;
   }


运行结果:

 

另一种可能常用的Adapter是SimpleAdapter。

构造函数:SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

context指定上下文

data指定存放数据的list对象,list中存放的是Map对象。

resource指定item的布局文件id

from和to是有序的,两者中的元素必须一一对应,from中存放的是list中每个map对象中的key值,to存放的是显示对应key获取的值的控件的id。

如:item_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="35sp"
        />
</LinearLayout>

MainActivity.java中修改如下:

protected void onCreate(Bundle savedInstanceState) {
           // TODO Auto-generated method stub
           super.onCreate(savedInstanceState);
           setContentView(R.layout.main_layout);
           ListView lv = (ListView) findViewById(R.id.listview);
           List<Map<String,Object>> data = new ArrayList<Map<String,Object>>();
           Map<String,Object> map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "aaa");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "bbb");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "ccc");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "ddd");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "eee");
           data.add(map);
          
           SimpleAdapter sa = new SimpleAdapter(this, data, R.layout.item_layout, new String[]{"img","str"}, new int[]{R.id.iv,R.id.tv_name});
           lv.setAdapter(sa);
}

显示结果:

 


关于ListView的事件监听:

AdapterView中定义了几个设置事件监听的方法,如:

void  setOnItemClickListener(AdapterView.OnItemClickListener listener)

void  setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener)

ListView间接继承了AdapterView,所以,要处理事件监听时,可以调用ListView中相关的方法。

注:如果调用了ListView的setOnClickListener()方法,会报如下错误:

java.lang.RuntimeException: Don't call setOnClickListener for an AdapterView. You probably want setOnItemClickListener instead

setOnItemClickListener()方法示例:

ListView lv = (ListView) findViewById(R.id.listview);
   lv.setOnItemClickListener(new OnItemClickListener() {
         @Override
         public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
              Log.i("listview","onClick");
              Log.i("listview",view.toString());
              Log.i("listview",position+"");
              Log.i("listview",id+"");
              TextView tv = (TextView) view.findViewById(R.id.tv_name);
              Toast.makeText(MainActivity.this, tv.getText(), Toast.LENGTH_LONG).show();
         }
   });


运行结果:

 


Intent用于启动Activity, Service, 以及BroadcastReceiver三种组件, 同时还是组件之间通信的重要媒介;IntentFilter类表示Intent过滤器, 大部分情况下, 每一个component都会定义一个或多个IntentFilter, 用于表明其可处理的Intent。

使用Intent启动组件的优势

1, Intent为组件的启动提供了一致的编程模型. 无论想要启动的组件是Activity, Service, 还是BroadcastReceiver, 都可以使用Intent封装启动的意图.
2, 在某些时候, 应用程序只是想启动具有某种特征的组件, 并不想和某个特定的组件耦合. 使用Intent可以方便的达到这种高层次解耦的目的.


IntentFilter类

IntentFilter类表示Intent过滤器, 大部分情况下, 每一个component都会定义一个或多个IntentFilter, 用于表明其可处理的Intent.
一般来说, component的IntentFilter应该在AndroidManifest.xml文件中定义.

关于Bundle:

注意到Activity的onCreate()方法的签名是protected void onCreate(Bundle savedInstanceState),其参数是一个Bundle实例。

Bundle以键值对的形式来存储数据,类似于Map,以便在Activity之间传递数据、状态信息。Bundle的键均为String类型,值可以是各种基本类型及可序列化的对象类型。

Bundle的常用方法:

构造方法:

Bundle()、Bundle(Bundle b)等

get系列根据键获取对应值的方法:

Object  get(String key)

boolean  getBoolean(String key)

boolean  getBoolean(String key, boolean defaultValue)

Bundle  getBundle(String key)

char  getChar(String key)

char  getChar(String key, char defaultValue)

int  getInt(String key)

int  getInt(String key, int defaultValue)

Serializable  getSerializable(String key)

等等

注:根据键到Bundle实例中查找对应值,若存在,则返回对应的值。不存在时,带有默认值的方法,返回传入的默认值;不带默认值的方法返回响应的零值,如false、null、0.0f、0等。

put系列存放键值对的方法:

void  putAll(Bundle map)

void  putBoolean(String key, boolean value)

void  putBundle(String key, Bundle value)

void  putChar(String key, char value)

void  putDouble(String key, double value)

void  putSerializable(String key, Serializable value)

等等

其他方法:

boolean  isEmpty()  判断Bundle实例是否为空。

Set<String>  keySet()  返回当前Bundle实例中key的集合。

int  size()  返回Bundle实例键值对的数目。

 

关于Intent:

注意到,在前面的程序中,从一个Activity启动另一个Activity,总是这样做:

ntent intent = new Intent(FirstActivity.this,  SecondActivity.class);
startActivity(intent);

可知,Intent可以用来表示一种启动Activity的意图,用来启动另一个Activity。

其实,Intent是一种消息传递机制,可以在应用程序中使用,也可以在应用程序之间使用。不仅可以用来启动Activity还可以用来启动Service和BroadcastReceiver。而且,还可以利用Intent与被启动的组件进行数据传输,信息交换。

Intent的构造方法:

Intent()

Intent(Intent o)

Intent(String action)

Intent(String action, Uri uri)

Intent(Context packageContext, Class<?> cls)

Intent(String action, Uri uri, Context packageContext, Class<?> cls)

Intent中的主要属性:

  private String mAction;

  private HashSet<String> mCategories;

  private Uri mData;

  private String mType;

  private ComponentName mComponent;

  private Bundle mExtras;

注意到,源码中属性的命名方式都是以m打头,表示这是一个member。

1)Component属性:指明了该Intent要启动的组件。该属性是一个ComponentName类型的对象,其构造方法有:

public ComponentName(String pkg, String cls):

pkg应为Manifest.xml中声明的Manifest节点的package属性声明的包名

cls应为要启动组件的完整类名:包名.类名的形式。

public ComponentName(Context pkg, String cls)

pkg为要启动的上下文环境,可以传入当前Activity实例的引用,如this。若是在内部类中,则应为MainActivity.this(注:MainActivity为当前Activity类名)

cls为要启动组件的完整类名:包名.类名的形式。

public ComponentName(Context pkg, Class<?> cls)

pkg为要启动的上下文环境,可以传入当前Activity实例的引用,如this。若是在内部类中,则应为MainActivity.this(注:MainActivity为当前Activity类名)

cls为要启动组件,如SecondActivity.class(注:SecondActivity为要启动的组件名)

总结:这三个构造方法都是要指定启动的上下文环境和一个要启动组件的完整类限定名,即传入包名.类名。由于在Android应用中包名是作为应用的唯一标识,所以包名与代表应用上下文环境的Context是一一对应的。

如:

第一个构造代码:

Intent intent1 = new Intent();

intent1.setComponent(new ComponentName("cn.csc.lifecycle", "cn.csc.lifecycle.NormalActivity"));

startActivity(intent1);

 

第二个构造代码:

 Intent intent1 = new Intent();

intent1.setComponent(new ComponentName(this, "cn.csc.lifecycle.NormalActivity"));

startActivity(intent1);

 

第三个构造代码:

Intent intent1 = new Intent();

intent1.setComponent(new ComponentName(this, NormalActivity.class));

startActivity(intent1);

 

Intent提供了一种简化的设置Component属性的方法,即:

Intent(Context packageContext, Class<?> cls)

这也是我们之前一直用的一种方式。

startActivity(new Intent(this, NormalActivity.class));

 

注意:设置了Component属性的Intent因为已然明确指定要启动的组件,故而被称之为显式Intent。当然,有显式Intent,肯定就有隐式Intent。

隐式Intent不指定Component属性,而是通过设置其他属性,来制定所要启动的组件应该具备的条件,然后,符合Intent其他属性所指定的条件的组件就会被启动。这时,运行时会使用一个称为“Intent解析”的过程来动态选择符合条件的组件。在Intent中设置要启动组件所需要具备的条件,就要用到Intent的mAction和mCategories等属性。

2)Action和Category属性:

源代码中属性的声明如下:

private String mAction;

private HashSet<String> mCategories;

可知,Action属性是一个普通的字符串,只能有一个,Category属性也是字符串,可以有多个,共同存放在一个名为mCategories的HashSet中。

其中,Action表示该Intent所要启动的组件应该能完成的抽象动作,比如Intent.ACTION_VIEW,可以完成查看动作,Intent.ACTION_DIAL可以完成拨号的动作等等。Category属性则用于为Action增加额外的附加类别信息。

如何让自己的组件具备Intent所要求的条件呢?

这里就需要在Manifest.xml中,注册应用所需组件,如Activity时,给Activity节点添加上<action>和<category>子节点,设置这两个节点的name属性,使其值与Intent中Action属性和Category属性一一对应即可。

如:

Intent intent1 = new Intent();

intent1.setAction("myaction");

startActivity(intent1);

 

要使此时自己定义的NormalActivity能被启动,则应在Manifest.xml中这样配置:

<activity android:name=".NormalActivity">
            <intent-filter >
                <action android:name="myaction"/>
            </intent-filter>
</activity>


运行程序,发现如下错误:

 


查看LogCat错误信息:

android.content.ActivityNotFoundException: No Activity found to handle Intent { act = myaction}

找不到能够接收处理action为”myaction”的Intent意图。

但是,我们明明在<intent-filter>中声明了的。

其实,这里只要修改一下<intent-filter>即可:

<intent-filter >
                <action android:name="myaction"/>
                <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>


这样,就能正常启动NormalActivity。

原因在于:当我们不明确设定Intent的Category属性时,其值默认为Intent.CATEGORY_DEFAULT常量,该常量值为"android.intent.category.DEFAULT"。

注意:Intent最多只能设置一个Action以及0~n个Category(注:0个其实,也是一个Intent.CATEGORY_DEFAULT常量);但是在Manifest.xml中为组件注册时,在<intent-filter>中可以指定多个<action>,多个<category>。只要该组件所注册的<action>和<category>能涵盖在Intent中设置的所有Action和Category即可被启动。

如上例中,修改<intetn-filter>如下:

<intent-filter >
                <action android:name="myaction"/>
                <action android:name="myaction1"/>
                <category android:name="mycategory"/>
                <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>


仍能被上面的Intent启动。

如,修改Intent如下:

Intent intent1 = new Intent();

intent1.setAction("myaction");

intent1.addCategory("mycategory");

startActivity(intent1);

仍然符合条件。

注意,设置Action的方式是setAction(),而由于Category可以有多个,设置的方式不是setCategory()而是addCategory()。

Intent也提供了对应的构造方法来简化Action的设置:

Intent(String action)

Intent(String action, Uri uri)

其中第二个构造函数的第二个参数为Uri对象,这就涉及到Intent的Data属性。

3)Data属性:

Data属性用于向Action属性所表示的动作提供所要操作的数据,如Action为Intent.ACTION_CALL表示要拨号,它对应的Data就应当为要拨打的号码。

Data属性接受一个Uri对象,通常以字符串”scheme://host:port/path”的形式表示:

如:”http://www.baidu.com” ,”tel:110”等。

如,修改Intent代码:

Intent intent1 = new Intent();

intent1.setAction(Intent.ACTION_CALL);

intent1.setData(Uri.parse("tel:110"));

startActivity(intent1);

将启动模拟器的拨号器,并拨打110

当然,在运行程序之间需要在Manifest.xml中Manifest节点下,添加<uses-permisson>子节点,配置该程序所需要的拨打电话的权限:

<uses-permission  android:name="android.permission.CALL_PHONE"/>



此时,如果修该NormalActivity的<intent-filter>

添加如下Action: <action android:name="android.intent.action.CALL"/>,想要让它也能被Intent启动,发现还是只启动拨号器。

这是因为,Intent设置了Data属性,要想让NormalActivity能被该Intent启动,也要在<intent-filter>中设置<data>子节点:<data android:scheme="tel"/>

此时,由于有多个组件具备Intent启动所要求的条件,故而出现选择列表,让用户来选择要启动的组件。

运行效果:

 


注意到,上面<data>子节点只设置了scheme属性,则所有scheme属性为tel的Data属性都能符合条件。

若修改Intent:

Intent intent1 = new Intent();

intent1.setAction(Intent.ACTION_VIEW);

intent1.setData(Uri.parse("http://www.baidu.com"));

startActivity(intent1);

此时,会启动浏览器,浏览http://www.baidu.com

 


修改NormalActivity的<intent-filter>添加

<action android:name="android.intent.action.VIEW"/>

 

修改<data>子节点: <data android:scheme="http"/>

重新运行:

 


若在<data>节点中设置android:host=“www.baidu.com”此时,也是可以启动NormalActivity的。但是,若Intent的Data属性换成”http://www.taobao.com”此时就不能启动NormalActivity了,因为它的<data>不符合条件。

只有<data>的属性与Data一致时,才能被Intent所启动。一般只指定android:scheme属性即可,不会给出太具体的属性。

注意:<intent-filter>节点中可以有0~N个<action>、0~N个<category>、0或1个<data>。

4)Type属性:用于指定该Data所指定的Uri对应的MIME类型,可以是任何自定义的MIME类型,只要符合abc/xyz格式的字符串就行。如:text/html等。

Data属性与Type属性会相互覆盖:谁最后被设置,谁就起作用,之前被设置的就被覆盖。若Data被覆盖,则getData()返回null,若Type被覆盖,getType()返回null。

Intent intent1 = new Intent();

intent1.setAction(Intent.ACTION_VIEW);

intent1.setData(Uri.parse("http://www.baidu.com"));

intent1.setType("text/html");

此时,调用intent1.getData()返回值为null。data节点若设置如下:

 <data android:mimeType="text/html"/> 则能被intent1启动。但是若:

 <data android:scheme="http" android:mimeType="text/html"/> ,这时就不能启动了,因为比Type值多出来个scheme属性。

若是希望同时设置Data和Type属性,则必须要使用setDataAndType()方法,如:

intent1.setDataAndType(Uri.parse("http://www.baidu.com"), "text/html");

 

此时,浏览器一定会被启动,若要NormalActivity也能被启动,其<data>节点,一定要同时设置android:scheme和android:mimeType属性:

<data  android:scheme="http" android:mimeType="text/html"/>

 5)Extras属性:

注意到private Bundle mExtras; mExtras的类型是Bundle。所以Intent可以借助其Bundle类型的mExtras属性在Activity之间进行数据传递。

Bundle  getExtras()  获取mExtras属性。

Intent  putExtras(Bundle extras)  设置mExtras属性。

可以调用Bundle的相关方法设置好要保存的键值对,然后再把Bundle实例传给putExtras()方法。Intent提供了更简单的键值对操作方式:

putExtra(String name, XXX  value) :向Intent中存入name-value的键值对,实际上是存入mExtras中。XXX表示不同的数据类型,如同Bundle中的putInt(),putDouble()等putXXX()。

getXxxExtra(String name):从Intent中按照key获取对应的值。实际上是到mExtras这个Bundle实例中去获取值。如同Bundle中的getInt(),getDouble()等getXxx()。

利用Intent在Activity之间传递数据:

前面曾经提到过Activity中有个名为getIntent()的方法,可以用来获取启动该Activity的Intent实例。

如,在MainActivity中获取启动它的Intent实例,并调用其toString()方法,将其设置为第一个按钮的text属性:

protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main_layout);

        Log.i("LIFECYCLE","onCreate");

        Button btnNormal = (Button) findViewById(R.id.normal);

        Button btnDialog = (Button) findViewById(R.id.dialog);

        btnNormal.setOnClickListener(this);

        btnDialog.setOnClickListener(this);

        btnNormal.setText(getIntent().toString());

}


从MainActivity向NormalActivity传递一个名为”data”,值为” data given by MainActivity”的数据:

Intent intent1 = new Intent(this, NormalActivity.class);
intent1.putExtra("data", "data given by MainActivity");
startActivity(intent1);

 
在NormalActivity中获取这个值,并将其设置为TextView的text属性:

protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.first_layout);
           TextView tv = (TextView) findViewById(R.id.tv);
           tv.setText(getIntent().getStringExtra("data"));
}



如果想要让被启动的Activity返回数据给启动它的那个Activity要怎么做呢?

startActivity()启动别的Activity之后就完全不再理它了,此时,就需要另外一个方法:

startActivityForResult(Intent intent, int requestCode):该方法用于根据intent参数启动一个Activity,并期望在被启动的Activity结束时,获得一个返回结果。

调用startActivityForResult()之后,被启动的Activity结束后返回前一个Activity时,会回调它的onActivityResult(int requestCode, int resultCode, Intent intent),其中,requestCode代表请求码,resultCode代表被启动Activity设置的返回结果码,intent里面包含了返回的数据。

被启动的Activity要给启动它的Activity返回数据时,需要调用setResult()方法设置要返回的数据。

如:MainActivity中使用startActivityForResult()启动NormalActivity,并重写onActivityResult()方法,接收NormalActivity传回的数据。

Intent intent1 = new Intent(this, NormalActivity.class);

intent1.putExtra("data", "data given by MainActivity");

startActivityForResult(intent1,0);

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
           // TODO Auto-generated method stub
           super.onActivityResult(requestCode, resultCode, intent);
           if(intent == null){
                 Log.i("tag","null");
           }else{
                 Log.i("tag",intent.getStringExtra("res"));
           }
}

NormalActivity重写返回按钮按下的回调函数onBackPressed()向MainActivity返回数据:

public void onBackPressed() {
           Log.i("tag","back pressed");
           setResult(Activity.RESULT_OK,getIntent().putExtra("res", "resultxxx"));
           super.onBackPressed();
}


此时,要注意,如果super.onBackPressed()放在该方法的第一句,无论后面设置什么值都不会被成功传递回去。

查看Activity的onBackPressed()方法的源代码,即可发现原因:

public void onBackPressed() {
        if (!mFragments.popBackStackImmediate()) {
            finish();
        }
}


这个方法会直接调用finish()直接结束该Activity实例。

public void finish() {
        if (mParent == null) {
            int resultCode;
            Intent resultData;
            synchronized (this) {
                resultCode = mResultCode;
                resultData = mResultData;
            }
            if (false) Log.v(TAG, "Finishing self: token=" + mToken);
            try {
                if (resultData != null) {
                    resultData.prepareToLeaveProcess();
                }
                if (ActivityManagerNative.getDefault()
                    .finishActivity(mToken, resultCode, resultData)) {
                    mFinished = true;
                }
            } catch (RemoteException e) {
                // Empty
            }
        } else {
            mParent.finishFromChild(this);
        }
}


阅读finish()的源码可以发现:若发现当前Activity需要返回数据,则直接传递null回去,然后结束掉当前Activity实例。

所以,无论我们在super.onBackPressed()后设置任何返回数据都不会被真正传回。

当然,也可以不调用super.onBackPressed(),在设置完成要返回的数据之后,自己调用一个finish()方法即可。



关于Intent的两点补充:

1.隐式Intent启动组件,会有一个Intent解析的过程,若找不到能够处理该Intent的组件,程序就会异常终止。一个合理的做法是,在使用Intent实例启动组件如:startActivity(intent)之前,最好能判断一下该调用能否解析为一个Activity。为了实现该功能,Intent提供了一个方法:

ComponentName  resolveActivity(PackageManager pm)  :该方法接收一个包管理器对象作为参数,通过查找该包管理器,返回能够处理该Intent的Activity的Component对象,没有找到能处理该Inent的组件时则返回null。

Intent intent1 = new Intent();
intent1.setAction(Intent.ACTION_VIEW);
intent1.setType("text/html");
Log.i("tag",intent1.toString());
ComponentName resolveActivity = intent1.resolveActivity(getPackageManager());
Log.i("tag",resolveActivity.getClassName());
Log.i("tag",resolveActivity.getPackageName());
Log.i("tag",resolveActivity.toString());
startActivity(intent1);



NormalActivity中配置<intent-filter>

<activity android:name=".NormalActivity">
            <intent-filter >
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="text/html"/>
            </intent-filter>
</activity>


运行信息:

 


可见解析到cn.csc.lifecycle.NormalActivity能够处理该Intent。

若找不到能够处理该Intent的组件时,会返回null,所以,安全的启动Activity的做法是:

if(intent1.resolveActivity(getPackageManager())!=null){
                      startActivity(intent1);
}


当然,可以加上找不到能处理intent1的组件的提示信息等。

注意,若用多个组件都能处理该Intent实例时,如:

Intent intent1 = new Intent();
intent1.setAction(Intent.ACTION_VIEW);
intent1.setData(Uri.parse("http://www.baidu.com"));
Log.i("tag",intent1.toString());
ComponentName resolveActivity = intent1.resolveActivity(getPackageManager());
Log.i("tag",resolveActivity.toString());
startActivity(intent1);
<activity android:name=".NormalActivity">
            <intent-filter >
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="http"/>
            </intent-filter>
</activity>


此时,浏览器和NormalActivity都能处理该Intent实例。

运行效果:

 


返回的Component信息为:

 


这个ResolverActivity其实就是一个选择打开程序的对话框,当有多个组件能处理Intent实例,但是没有设置默认的处理程序时,就会交由ResolverActivty来处理,而ResolverActivity的做法就是把所有能处理的都显示在一个列表中,由用户决定采用哪个来处理。当我们勾选了下面的use by default for this action时,之后选择的组件将会作为该Intent的默认处理程序,该设置会保存在手机或者模拟器中,下次在出现该Intent时,直接使用设置的默认组件去处理,而不再转到ResolverActivity。

如,这里我选择了Browser作为默认处理程序,下次再启动改程序,输出的运行信息:
 
 

2.关于使用Intent在Activity之间传递数据,在之前都是传递些内置类型的数据,若想传递自定义类型的数据,应该怎么办呢?

注意到在Intent中有下面两对方法:

Serializable  getSerializableExtra(String name)

Intent  putExtra(String name, Serializable value)

放入/获取Serializable类型的值。

<T extends Parcelable> T  getParcelableExtra(String name)

Intent  putExtra(String name, Parcelable value)

放入/获取Parcelable类型的值。

Serializable和Parcelable都是接口,即要想使用Intent传递自定义类型的数据,则自定义类型的数据应当事先Serializable接口或者Parcelable接口。

Serializable接口:

java.io.Serializable是一个空的起到标记作用的接口。标记实现该接口,只需要在类定义时声明实现该接口即可。实现该接口的类表明能够被序列化和反序列化,对大多数的类来说已然足够了,当然,若想更细粒度的操纵序列化过程,可以实现下面这两个方法:

private void writeObject(java.io.ObjectOutputStream out) throws IOException

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException

一般情况下,都用不到的。

这里简单声明实现该接口,如:

import java.io.Serializable;
 
public class MyData implements Serializable {
      private String stringData;
      private int intData;
public MyData(String stringData, int intData) {
           super();
           this.stringData = stringData;
           this.intData = intData;
      }
      public String getStringData() {
           return stringData;
      }
      public void setStringData(String stringData) {
           this.stringData = stringData;
      }
      public int getIntData() {
           return intData;
      }
      public void setIntData(int intData) {
           this.intData = intData;
      }
}


之后,便可以传递MyData类型数据了:

MyData  data1 = new MyData(“aaa”,111);

Intent intent1 = new Intent();

intent1.putExtra(“mydata”, data1);

 

取得该数据:

MyData  data1 = (MyData)getIntent.getSerializableExtra(“data1”);

 

Parcelable接口:相比Serializable要复杂一点,需要实现里面的抽象方法。

android.os.Parcelable接口,帮助文档中,有一个典型的实现例子如下,可以用作参考,自己实现该接口时,模仿即可:

public class MyParcelable implements Parcelable {    
      private int mData;    
      public int describeContents() {        
           return 0;    
           }    
      public void writeToParcel(Parcel out, int flags) {
           out.writeInt(mData);    
           }    
      public static final Parcelable.Creator<MyParcelable> CREATOR            
      = new Parcelable.Creator<MyParcelable>() {        
           public MyParcelable createFromParcel(Parcel in) {
                 return new MyParcelable(in);         
                 }        
           public MyParcelable[] newArray(int size) {            
                 return new MyParcelable[size];        
                 }    
           };         
      private MyParcelable(Parcel in) {      
           mData = in.readInt();    
      }
}


定义各种内置类型的数据,然后在writeToParcel分别根据不同的类型调用对应类型的

out.writeXXX(mData);在构造方法中,分别调用in.readXX()即可。

然后就可以调用Intent  putExtra(String name, Parcelable value)去放入该自定义类型数据,调用<T extends Parcelable> T  getParcelableExtra(String name)获取该自定义类型数据。

[!--infotagslink--]

相关文章

  • C#创建自定义控件及添加自定义属性和事件使用实例详解

    这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
  • 解决echarts 一条柱状图显示两个值,类似进度条的问题

    这篇文章主要介绍了解决echarts 一条柱状图显示两个值,类似进度条的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-20
  • JS实现自定义简单网页软键盘效果代码

    本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
  • jQuery实现简单的文件上传进度条效果

    本文实例讲述了jQuery实现文件上传进度条效果的代码。分享给大家供大家参考。具体如下: 运行效果截图如下:具体代码如下:<!DOCTYPE html><html><head><meta charset="utf-8"><title>upload</title><link rel="stylesheet...2015-11-24
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • ant design中upload组件上传大文件,显示进度条进度的实例

    这篇文章主要介绍了ant design中upload组件上传大文件,显示进度条进度的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-29
  • jquery实现模拟百分比进度条渐变效果代码

    本文实例讲述了jquery实现模拟百分比进度条渐变效果代码。分享给大家供大家参考,具体如下:这里为了便于看到加载百分比,对代码进行了处理,实际使用时并不需要这样。运行效果截图如下:在线演示地址如下:http://demo.jb51.net...2015-10-30
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • 自定义jquery模态窗口插件无法在顶层窗口显示问题

    自定义一个jquery模态窗口插件,将它集成到现有平台框架中时,它只能在mainFrame窗口中显示,无法在顶层窗口显示. 解决这个问题的办法: 通过以下代码就可能实现在顶层窗口弹窗 复制代码 代码如下: $(window.top.documen...2014-05-31
  • 自定义feignClient的常见坑及解决

    这篇文章主要介绍了自定义feignClient的常见坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-20
  • pytorch 自定义卷积核进行卷积操作方式

    今天小编就为大家分享一篇pytorch 自定义卷积核进行卷积操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-05-06
  • PHP YII框架开发小技巧之模型(models)中rules自定义验证规则

    YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证...2015-11-24
  • jquery自定义插件开发之window的实现过程

    这篇文章主要介绍了jquery自定义插件开发之window的实现过程的相关资料,需要的朋友可以参考下...2016-05-09
  • C#实现带进度条的ListView

    这篇文章主要介绍了C#实现带进度条的ListView 的相关资料,需要的朋友可以参考下...2020-06-25
  • nodejs 终端打印进度条实例代码

    本篇文章主要介绍了nodejs 终端打印进度条实例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...2017-04-27
  • PHP的APC模块实现上传进度条

    APC模块,它的全称是Alternative PHP Cache。APC可以将所有PHP代码会被缓存起来, 另外它可提供一定的内存缓存功能.但是这个功能并不是十分完美,有报告说如果频繁使用APC缓存的写入功能,会导致不可预料的错误.如果想使用...2015-10-30
  • Bootstrap进度条组件知识详解

    在网页中,经常见到进度条效果,那么这些个性的进度条组件效果是怎么实现的呢,下面脚本之家小编给大家分享Bootstrap进度条组件知识详解,感兴趣的朋友要求学习吧...2016-05-04
  • php使用APC实现实时上传进度条功能

    php不具备实时上传进度条功能,如果想有这种功能我们一般会使用ajax来实现,但是php提供了一个apc,它就可以与php配置实现上传进度条功能。 主要针对的是window上的应用。 1.服务器要支持apc扩展,没有此扩展的话,下载一个扩...2015-10-30
  • C#自定义事件监听实现方法

    这篇文章主要介绍了C#自定义事件监听实现方法,涉及C#事件监听的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • Vue 组件复用多次自定义参数操作

    这篇文章主要介绍了Vue 组件复用多次自定义参数操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-27