فهرست منبع

1.添加折叠布局控件;
2.添加扫描设备活动;
3.添加图标;
4.修改主页布局。

Yumin 6 سال پیش
والد
کامیت
d5679d7608

+ 4 - 0
app/src/main/AndroidManifest.xml

@@ -21,12 +21,16 @@
             android:theme="@style/FullscreenTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
+
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
         <activity
             android:name=".views.MainActivity"
             android:label="@string/app_name" />
+        <activity
+            android:name=".views.ScanDeviceActivity"
+            android:label="设备连接" />
     </application>
 
 </manifest>

+ 611 - 0
app/src/main/java/cn/minbb/producttester/controls/BaseFoldingLayout.java

@@ -0,0 +1,611 @@
+package cn.minbb.producttester.controls;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import cn.minbb.producttester.R;
+
+public class BaseFoldingLayout extends ViewGroup {
+
+    public static enum Orientation {
+        VERTICAL, HORIZONTAL
+    }
+
+    private final String FOLDING_VIEW_EXCEPTION_MESSAGE = "Folding Layout can only 1 child at most";
+
+    private final float SHADING_ALPHA = 0.8f;
+    private final float SHADING_FACTOR = 0.5f;
+    private final int DEPTH_CONSTANT = 1500;
+    private final int NUM_OF_POLY_POINTS = 8;
+
+    private Rect[] mFoldRectArray;
+
+    private Matrix[] mMatrix;
+
+    protected Orientation mOrientation = Orientation.VERTICAL;
+
+    protected float mAnchorFactor = 0;
+    private float mFoldFactor = 0;
+
+    private int mNumberOfFolds = 2;
+
+    private boolean mIsHorizontal = true;
+
+    private int mOriginalWidth = 0;
+    private int mOriginalHeight = 0;
+
+    private float mFoldMaxWidth = 0;
+    private float mFoldMaxHeight = 0;
+    private float mFoldDrawWidth = 0;
+    private float mFoldDrawHeight = 0;
+
+    private boolean mIsFoldPrepared = false;
+    private boolean mShouldDraw = true;
+
+    private Paint mSolidShadow;
+    private Paint mGradientShadow;
+    private LinearGradient mShadowLinearGradient;
+    private Matrix mShadowGradientMatrix;
+
+    private float[] mSrc;
+    private float[] mDst;
+
+    private OnFoldListener mFoldListener;
+
+    private float mPreviousFoldFactor = 0;
+
+    private Bitmap mFullBitmap;
+    private Rect mDstRect;
+
+    public BaseFoldingLayout(Context context) {
+        super(context);
+    }
+
+    public BaseFoldingLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs);
+    }
+
+    public BaseFoldingLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init(context, attrs);
+    }
+
+    public void init(Context context, AttributeSet attrs) {
+        // now style everything!
+        TypedArray ta = context.obtainStyledAttributes(attrs,
+                R.styleable.FoldingMenu);
+        int mFoldNumber = ta.getInt(R.styleable.FoldingMenu_foldNumber,
+                mNumberOfFolds);
+        if (mFoldNumber > 0 && mFoldNumber < 7) {
+            mNumberOfFolds = mFoldNumber;
+        } else {
+            mNumberOfFolds = 2;
+        }
+        ta.recycle();
+    }
+
+    @Override
+    protected boolean addViewInLayout(View child, int index,
+                                      LayoutParams params, boolean preventRequestLayout) {
+        throwCustomException(getChildCount());
+        boolean returnValue = super.addViewInLayout(child, index, params,
+                preventRequestLayout);
+        return returnValue;
+    }
+
+    @Override
+    public void addView(View child, int index, LayoutParams params) {
+        throwCustomException(getChildCount());
+        super.addView(child, index, params);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        View child = getChildAt(0);
+        measureChild(child, widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        View child = getChildAt(0);
+        child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
+        updateFold();
+    }
+
+    /**
+     * The custom exception to be thrown so as to limit the number of views in
+     * this layout to at most one.
+     */
+    private class NumberOfFoldingLayoutChildrenException extends
+            RuntimeException {
+        /**
+         *
+         */
+        private static final long serialVersionUID = 1L;
+
+        public NumberOfFoldingLayoutChildrenException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Throws an exception if the number of views added to this layout exceeds
+     * one.
+     */
+    private void throwCustomException(int numOfChildViews) {
+        if (numOfChildViews == 1) {
+            throw new NumberOfFoldingLayoutChildrenException(
+                    FOLDING_VIEW_EXCEPTION_MESSAGE);
+        }
+    }
+
+    public void setFoldListener(OnFoldListener foldListener) {
+        mFoldListener = foldListener;
+    }
+
+    /**
+     * Sets the fold factor of the folding view and updates all the
+     * corresponding matrices and values to account for the new fold factor.
+     * Once that is complete, it redraws itself with the new fold.
+     */
+    public void setFoldFactor(float foldFactor) {
+        if (foldFactor != mFoldFactor) {
+            mFoldFactor = foldFactor;
+            calculateMatrices();
+            invalidate();
+        }
+    }
+
+
+    public void setOrientation(Orientation orientation) {
+        if (orientation != mOrientation) {
+            mOrientation = orientation;
+            updateFold();
+        }
+    }
+
+    public void setAnchorFactor(float anchorFactor) {
+        if (anchorFactor != mAnchorFactor) {
+            mAnchorFactor = anchorFactor;
+            updateFold();
+        }
+    }
+
+    public void setNumberOfFolds(int numberOfFolds) {
+        if (numberOfFolds != mNumberOfFolds) {
+            mNumberOfFolds = numberOfFolds;
+            updateFold();
+        }
+    }
+
+    public float getAnchorFactor() {
+        return mAnchorFactor;
+    }
+
+    public Orientation getOrientation() {
+        return mOrientation;
+    }
+
+    public float getFoldFactor() {
+        return mFoldFactor;
+    }
+
+    public int getNumberOfFolds() {
+        return mNumberOfFolds;
+    }
+
+    private void updateFold() {
+        prepareFold(mOrientation, mAnchorFactor, mNumberOfFolds);
+        calculateMatrices();
+        invalidate();
+    }
+
+    /**
+     * This method is called in order to update the fold's orientation, anchor
+     * point and number of folds. This creates the necessary setup in order to
+     * prepare the layout for a fold with the specified parameters. Some of the
+     * dimensions required for the folding transformation are also acquired
+     * here.
+     * <p>
+     * After this method is called, it will be in a completely unfolded state by
+     * default.
+     */
+    private void prepareFold(Orientation orientation, float anchorFactor,
+                             int numberOfFolds) {
+
+        mSrc = new float[NUM_OF_POLY_POINTS];
+        mDst = new float[NUM_OF_POLY_POINTS];
+
+        mDstRect = new Rect();
+
+        mFoldFactor = 0;
+        mPreviousFoldFactor = 0;
+
+        mIsFoldPrepared = false;
+
+        mSolidShadow = new Paint();
+        mGradientShadow = new Paint();
+
+        mOrientation = orientation;
+        mIsHorizontal = (orientation == Orientation.HORIZONTAL);
+
+        if (mIsHorizontal) {
+            mShadowLinearGradient = new LinearGradient(0, 0, SHADING_FACTOR, 0,
+                    Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP);
+        } else {
+            mShadowLinearGradient = new LinearGradient(0, 0, 0, SHADING_FACTOR,
+                    Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP);
+        }
+
+        mGradientShadow.setStyle(Style.FILL);
+        mGradientShadow.setShader(mShadowLinearGradient);
+        mShadowGradientMatrix = new Matrix();
+
+        mAnchorFactor = anchorFactor;
+        mNumberOfFolds = numberOfFolds;
+
+        mOriginalWidth = getMeasuredWidth();
+        mOriginalHeight = getMeasuredHeight();
+
+        mFoldRectArray = new Rect[mNumberOfFolds];
+        mMatrix = new Matrix[mNumberOfFolds];
+
+        for (int x = 0; x < mNumberOfFolds; x++) {
+            mMatrix[x] = new Matrix();
+        }
+
+        int h = mOriginalHeight;
+        int w = mOriginalWidth;
+
+        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2 && h != 0 && w != 0) {
+            mFullBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(mFullBitmap);
+            getChildAt(0).draw(canvas);
+        }
+
+        int delta = Math.round(mIsHorizontal ? ((float) w)
+                / ((float) mNumberOfFolds) : ((float) h)
+                / ((float) mNumberOfFolds));
+
+        /*
+         * Loops through the number of folds and segments the full layout into a
+         * number of smaller equal components. If the number of folds is odd,
+         * then one of the components will be smaller than all the rest. Note
+         * that deltap below handles the calculation for an odd number of folds.
+         */
+        for (int x = 0; x < mNumberOfFolds; x++) {
+            if (mIsHorizontal) {
+                int deltap = (x + 1) * delta > w ? w - x * delta : delta;
+                mFoldRectArray[x] = new Rect(x * delta, 0, x * delta + deltap,
+                        h);
+            } else {
+                int deltap = (x + 1) * delta > h ? h - x * delta : delta;
+                mFoldRectArray[x] = new Rect(0, x * delta, w, x * delta
+                        + deltap);
+            }
+        }
+
+        if (mIsHorizontal) {
+            mFoldMaxHeight = h;
+            mFoldMaxWidth = delta;
+        } else {
+            mFoldMaxHeight = delta;
+            mFoldMaxWidth = w;
+        }
+
+        mIsFoldPrepared = true;
+    }
+
+    /*
+     * Calculates the transformation matrices used to draw each of the separate
+     * folding segments from this view.
+     */
+    private void calculateMatrices() {
+
+        mShouldDraw = true;
+
+        if (!mIsFoldPrepared) {
+            return;
+        }
+
+        /**
+         * If the fold factor is 1 than the folding view should not be seen and
+         * the canvas can be left completely empty.
+         */
+        if (mFoldFactor == 1) {
+            mShouldDraw = false;
+            return;
+        }
+
+//		if (mFoldFactor == 0 && mPreviousFoldFactor > 0
+//				&& mFoldListener != null) {
+//
+//			mFoldListener.onEndFold(mFoldFactor);
+//		}else if (mPreviousFoldFactor == 0 && mFoldFactor > 0
+//				&& mFoldListener != null) {
+//
+//			mFoldListener.onStartFold(mFoldFactor);
+//		} else if(mFoldListener != null) {
+//		    mFoldListener.onFoldingState(mFoldFactor);
+//		}
+
+        if (mFoldFactor > 0.9f && mFoldFactor > mPreviousFoldFactor
+                && mFoldListener != null) {
+
+            mFoldListener.onEndFold(mFoldFactor);
+        } else if (mFoldFactor < 0.1f && mFoldFactor < mPreviousFoldFactor
+                && mFoldListener != null) {
+
+            mFoldListener.onStartFold(mFoldFactor);
+        } else if (mFoldListener != null) {
+
+            mFoldListener.onFoldingState(mFoldFactor, mFoldDrawHeight);
+        }
+
+//		if(mFoldFactor == 0 && mFoldListener != null) {
+//            mFoldListener.onStartFold(mFoldFactor);
+//        } else if(mFoldFactor == 1 && mFoldListener != null) {
+//            mFoldListener.onEndFold(mFoldFactor);
+//        } else {
+//            mFoldListener.onFoldingState(mFoldFactor);
+//        }
+
+        mPreviousFoldFactor = mFoldFactor;
+
+        /*
+         * Reset all the transformation matrices back to identity before
+         * computing the new transformation
+         */
+        for (int x = 0; x < mNumberOfFolds; x++) {
+            mMatrix[x].reset();
+        }
+
+        float cTranslationFactor = 1 - mFoldFactor;
+
+        float translatedDistance = mIsHorizontal ? mOriginalWidth
+                * cTranslationFactor : mOriginalHeight * cTranslationFactor;
+
+        float translatedDistancePerFold = Math.round(translatedDistance
+                / mNumberOfFolds);
+
+        /*
+         * For an odd number of folds, the rounding error may cause the
+         * translatedDistancePerFold to be grater than the max fold width or
+         * height.
+         */
+        mFoldDrawWidth = mFoldMaxWidth < translatedDistancePerFold ? translatedDistancePerFold
+                : mFoldMaxWidth;
+        mFoldDrawHeight = mFoldMaxHeight < translatedDistancePerFold ? translatedDistancePerFold
+                : mFoldMaxHeight;
+
+        float translatedDistanceFoldSquared = translatedDistancePerFold
+                * translatedDistancePerFold;
+
+        /*
+         * Calculate the depth of the fold into the screen using pythagorean
+         * theorem.
+         */
+        float depth = mIsHorizontal ? (float) Math
+                .sqrt((double) (mFoldDrawWidth * mFoldDrawWidth - translatedDistanceFoldSquared))
+                : (float) Math
+                .sqrt((double) (mFoldDrawHeight * mFoldDrawHeight - translatedDistanceFoldSquared));
+
+        /*
+         * The size of some object is always inversely proportional to the
+         * distance it is away from the viewpoint. The constant can be varied to
+         * to affect the amount of perspective.
+         */
+        float scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + depth);
+
+        float scaledWidth, scaledHeight, bottomScaledPoint, topScaledPoint, rightScaledPoint, leftScaledPoint;
+
+        if (mIsHorizontal) {
+            scaledWidth = mFoldDrawWidth * cTranslationFactor;
+            scaledHeight = mFoldDrawHeight * scaleFactor;
+        } else {
+            scaledWidth = mFoldDrawWidth * scaleFactor;
+            scaledHeight = mFoldDrawHeight * cTranslationFactor;
+        }
+
+        topScaledPoint = (mFoldDrawHeight - scaledHeight) / 2.0f;
+        bottomScaledPoint = topScaledPoint + scaledHeight;
+
+        leftScaledPoint = (mFoldDrawWidth - scaledWidth) / 2.0f;
+        rightScaledPoint = leftScaledPoint + scaledWidth;
+
+        float anchorPoint = mIsHorizontal ? mAnchorFactor * mOriginalWidth
+                : mAnchorFactor * mOriginalHeight;
+
+        /* The fold along which the anchor point is located. */
+        float midFold = mIsHorizontal ? (anchorPoint / mFoldDrawWidth)
+                : anchorPoint / mFoldDrawHeight;
+
+        mSrc[0] = 0;
+        mSrc[1] = 0;
+        mSrc[2] = 0;
+        mSrc[3] = mFoldDrawHeight;
+        mSrc[4] = mFoldDrawWidth;
+        mSrc[5] = 0;
+        mSrc[6] = mFoldDrawWidth;
+        mSrc[7] = mFoldDrawHeight;
+
+        /*
+         * Computes the transformation matrix for each fold using the values
+         * calculated above.
+         */
+        for (int x = 0; x < mNumberOfFolds; x++) {
+
+            boolean isEven = (x % 2 == 0);
+
+            if (mIsHorizontal) {
+                mDst[0] = (anchorPoint > x * mFoldDrawWidth) ? anchorPoint
+                        + (x - midFold) * scaledWidth : anchorPoint
+                        - (midFold - x) * scaledWidth;
+                mDst[1] = isEven ? 0 : topScaledPoint;
+                mDst[2] = mDst[0];
+                mDst[3] = isEven ? mFoldDrawHeight : bottomScaledPoint;
+                mDst[4] = (anchorPoint > (x + 1) * mFoldDrawWidth) ? anchorPoint
+                        + (x + 1 - midFold) * scaledWidth
+                        : anchorPoint - (midFold - x - 1) * scaledWidth;
+                mDst[5] = isEven ? topScaledPoint : 0;
+                mDst[6] = mDst[4];
+                mDst[7] = isEven ? bottomScaledPoint : mFoldDrawHeight;
+
+            } else {
+                mDst[0] = isEven ? 0 : leftScaledPoint;
+                mDst[1] = (anchorPoint > x * mFoldDrawHeight) ? anchorPoint
+                        + (x - midFold) * scaledHeight : anchorPoint
+                        - (midFold - x) * scaledHeight;
+                mDst[2] = isEven ? leftScaledPoint : 0;
+                mDst[3] = (anchorPoint > (x + 1) * mFoldDrawHeight) ? anchorPoint
+                        + (x + 1 - midFold) * scaledHeight
+                        : anchorPoint - (midFold - x - 1) * scaledHeight;
+                mDst[4] = isEven ? mFoldDrawWidth : rightScaledPoint;
+                mDst[5] = mDst[1];
+                mDst[6] = isEven ? rightScaledPoint : mFoldDrawWidth;
+                mDst[7] = mDst[3];
+            }
+
+            /*
+             * Pixel fractions are present for odd number of folds which need to
+             * be rounded off here.
+             */
+            for (int y = 0; y < 8; y++) {
+                mDst[y] = Math.round(mDst[y]);
+            }
+
+            /*
+             * If it so happens that any of the folds have reached a point where
+             * the width or height of that fold is 0, then nothing needs to be
+             * drawn onto the canvas because the view is essentially completely
+             * folded.
+             */
+            if (mIsHorizontal) {
+                if (mDst[4] <= mDst[0] || mDst[6] <= mDst[2]) {
+                    mShouldDraw = false;
+                    return;
+                }
+            } else {
+                if (mDst[3] <= mDst[1] || mDst[7] <= mDst[5]) {
+                    mShouldDraw = false;
+                    return;
+                }
+            }
+
+            /* Sets the shadow and bitmap transformation matrices. */
+            mMatrix[x].setPolyToPoly(mSrc, 0, mDst, 0, NUM_OF_POLY_POINTS / 2);
+        }
+        /*
+         * The shadows on the folds are split into two parts: Solid shadows and
+         * gradients. Every other fold has a solid shadow which overlays the
+         * whole fold. Similarly, the folds in between these alternating folds
+         * also have an overlaying shadow. However, it is a gradient that takes
+         * up part of the fold as opposed to a solid shadow overlaying the whole
+         * fold.
+         */
+
+        /* Solid shadow paint object. */
+        int alpha = (int) (mFoldFactor * 255 * SHADING_ALPHA);
+
+        mSolidShadow.setColor(Color.argb(alpha, 0, 0, 0));
+
+        if (mIsHorizontal) {
+            mShadowGradientMatrix.setScale(mFoldDrawWidth, 1);
+            mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
+        } else {
+            mShadowGradientMatrix.setScale(1, mFoldDrawHeight);
+            mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
+        }
+
+        mGradientShadow.setAlpha(alpha);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        /**
+         * If prepareFold has not been called or if preparation has not
+         * completed yet, then no custom drawing will take place so only need to
+         * invoke super's onDraw and return.
+         */
+        if (!mIsFoldPrepared || mFoldFactor == 0) {
+            super.dispatchDraw(canvas);
+            return;
+        }
+
+        if (!mShouldDraw) {
+            return;
+        }
+
+        Rect src;
+        /*
+         * Draws the bitmaps and shadows on the canvas with the appropriate
+         * transformations.
+         */
+        for (int x = 0; x < mNumberOfFolds; x++) {
+
+            src = mFoldRectArray[x];
+            /* The canvas is saved and restored for every individual fold */
+            canvas.save();
+
+            /*
+             * Concatenates the canvas with the transformation matrix for the
+             * the segment of the view corresponding to the actual image being
+             * displayed.
+             */
+            canvas.concat(mMatrix[x]);
+            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2) {
+                mDstRect.set(0, 0, src.width(), src.height());
+                canvas.drawBitmap(mFullBitmap, src, mDstRect, null);
+            } else {
+                /*
+                 * The same transformation matrix is used for both the shadow
+                 * and the image segment. The canvas is clipped to account for
+                 * the size of each fold and is translated so they are drawn in
+                 * the right place. The shadow is then drawn on top of the
+                 * different folds using the sametransformation matrix.
+                 */
+                canvas.clipRect(0, 0, src.right - src.left, src.bottom
+                        - src.top);
+
+                if (mIsHorizontal) {
+                    canvas.translate(-src.left, 0);
+                } else {
+                    canvas.translate(0, -src.top);
+                }
+
+                super.dispatchDraw(canvas);
+
+                if (mIsHorizontal) {
+                    canvas.translate(src.left, 0);
+                } else {
+                    canvas.translate(0, src.top);
+                }
+            }
+            /* Draws the shadows corresponding to this specific fold. */
+            if (x % 2 == 0) {
+                canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight,
+                        mSolidShadow);
+            } else {
+                canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight,
+                        mGradientShadow);
+            }
+
+            canvas.restore();
+        }
+    }
+}

+ 157 - 0
app/src/main/java/cn/minbb/producttester/controls/FoldingLayout.java

@@ -0,0 +1,157 @@
+package cn.minbb.producttester.controls;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+
+public class FoldingLayout extends BaseFoldingLayout {
+
+    private final String FOLDING_VIEW_EXCEPTION_MESSAGE = "Folding Layout can only 1 child at most";
+
+    private GestureDetector mScrollGestureDetector;
+
+    FoldingLayout that = null;
+
+    private int mTranslation = 0;
+    private int mParentPositionY = -1;
+    private int mTouchSlop = -1;
+    private boolean mDidNotStartScroll = true;
+
+    public FoldingLayout(Context context) {
+        super(context);
+        init(context, null);
+        that = this;
+    }
+
+    public FoldingLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs);
+        that = this;
+    }
+
+    public FoldingLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init(context, attrs);
+        that = this;
+    }
+
+    public void init(Context context, AttributeSet attrs) {
+        mScrollGestureDetector = new GestureDetector(context, new ScrollGestureDetector());
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        setAnchorFactor(0);
+        super.init(context, attrs);
+    }
+
+    @Override
+    protected boolean addViewInLayout(View child, int index, ViewGroup.LayoutParams params, boolean preventRequestLayout) {
+        throwCustomException(getChildCount());
+        boolean returnValue = super.addViewInLayout(child, index, params, preventRequestLayout);
+        return returnValue;
+    }
+
+    /**
+     * The custom exception to be thrown so as to limit the number of views in
+     * this layout to at most one.
+     */
+    private class NumberOfFoldingLayoutChildrenException extends
+            RuntimeException {
+        /**
+         *
+         */
+        private static final long serialVersionUID = 1L;
+
+        public NumberOfFoldingLayoutChildrenException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Throws an exception if the number of views added to this layout exceeds one.
+     */
+    private void throwCustomException(int numOfChildViews) {
+        if (numOfChildViews == 1) {
+            throw new NumberOfFoldingLayoutChildrenException(FOLDING_VIEW_EXCEPTION_MESSAGE);
+        }
+    }
+
+    /**
+     * This class uses user touch events to fold and unfold the folding view.
+     */
+    private class ScrollGestureDetector extends GestureDetector.SimpleOnGestureListener {
+        @Override
+        public boolean onDown(MotionEvent e) {
+            mDidNotStartScroll = true;
+            return true;
+        }
+
+        /**
+         * All the logic here is used to determine by what factor the paper view
+         * should be folded in response to the user's touch events. The logic
+         * here uses vertical scrolling to fold a vertically oriented view and
+         * horizontal scrolling to fold a horizontally oriented fold. Depending
+         * on where the anchor point of the fold is, movements towards or away
+         * from the anchor point will either fold or unfold the paper
+         * respectively.
+         * <p>
+         * The translation logic here also accounts for the touch slop when a
+         * new user touch begins, but before a scroll event is first invoked.
+         */
+        @Override
+        public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                                float distanceX, float distanceY) {
+            int touchSlop = 0;
+            float factor;
+            if (mOrientation == Orientation.VERTICAL) {
+                factor = Math.abs((float) (mTranslation) / (float) (that.getHeight()));
+                if (e2.getY() - mParentPositionY <= that.getHeight() && e2.getY() - mParentPositionY >= 0) {
+                    if ((e2.getY() - mParentPositionY) > that.getHeight() * getAnchorFactor()) {
+                        mTranslation -= (int) distanceY;
+                        touchSlop = distanceY < 0 ? -mTouchSlop : mTouchSlop;
+                    } else {
+                        mTranslation += (int) distanceY;
+                        touchSlop = distanceY < 0 ? mTouchSlop : -mTouchSlop;
+                    }
+                    mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
+
+                    if (mTranslation < -that.getHeight()) {
+                        mTranslation = -that.getHeight();
+                    }
+                }
+            } else {
+                factor = Math.abs(((float) mTranslation) / ((float) that.getWidth()));
+                if (e2.getRawX() > that.getWidth() * getAnchorFactor()) {
+                    mTranslation -= (int) distanceX;
+                    touchSlop = distanceX < 0 ? -mTouchSlop : mTouchSlop;
+                } else {
+                    mTranslation += (int) distanceX;
+                    touchSlop = distanceX < 0 ? mTouchSlop : -mTouchSlop;
+                }
+                mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
+
+                if (mTranslation < -that.getWidth()) {
+                    mTranslation = -that.getWidth();
+                }
+            }
+
+            mDidNotStartScroll = false;
+
+            if (mTranslation > 0) {
+                mTranslation = 0;
+            }
+
+            that.setFoldFactor(factor);
+
+            return true;
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+        return mScrollGestureDetector.onTouchEvent(me);
+    }
+
+}

+ 9 - 0
app/src/main/java/cn/minbb/producttester/controls/OnFoldListener.java

@@ -0,0 +1,9 @@
+package cn.minbb.producttester.controls;
+
+public interface OnFoldListener {
+    public void onStartFold(float foldFactor);
+
+    public void onFoldingState(float foldFactor, float foldDrawHeight);
+
+    public void onEndFold(float foldFactor);
+}

+ 28 - 0
app/src/main/java/cn/minbb/producttester/views/MainActivity.java

@@ -1,5 +1,6 @@
 package cn.minbb.producttester.views;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
@@ -8,24 +9,35 @@ import android.support.design.widget.BottomNavigationView;
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.TextView;
+
+import com.joanzapata.iconify.widget.IconTextView;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import butterknife.BindView;
 import butterknife.ButterKnife;
+import butterknife.OnClick;
 import cn.minbb.producttester.R;
 import cn.minbb.producttester.adapter.MainPagerAdapter;
 
 public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
 
+    @BindView(R.id.toolbar)
+    Toolbar toolbar;
     @BindView(R.id.viewPager)
     ViewPager viewPager;
     @BindView(R.id.navigation)
     BottomNavigationView navigation;
+    @BindView(R.id.icon)
+    IconTextView icon;
+    @BindView(R.id.title)
+    TextView title;
 
     private List<View> views = new ArrayList<>(3);
 
@@ -44,6 +56,7 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationV
         loadData();
     }
 
+    @SuppressLint("InflateParams")
     private void initView() {
         LayoutInflater layoutInflater = LayoutInflater.from(this);
         View page_1, page_2, page_3;
@@ -92,17 +105,32 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationV
         context.startActivity(starter);
     }
 
+    @OnClick(R.id.noDevice)
+    public void onViewClicked(View view) {
+        switch (view.getId()) {
+            case R.id.noDevice:
+                ScanDeviceActivity.start(MainActivity.this);
+                break;
+        }
+    }
+
     @Override
     public boolean onNavigationItemSelected(@NonNull MenuItem item) {
         switch (item.getItemId()) {
             case R.id.navigation_1:
                 viewPager.setCurrentItem(0);
+                icon.setText("{fa-server 20sp}");
+                title.setText("设备");
                 return true;
             case R.id.navigation_2:
                 viewPager.setCurrentItem(1);
+                icon.setText("{fa-pie-chart 20sp}");
+                title.setText("数据");
                 return true;
             case R.id.navigation_3:
                 viewPager.setCurrentItem(2);
+                icon.setText("{fa-cubes 20sp}");
+                title.setText("扩展");
                 return true;
         }
         return false;

+ 161 - 0
app/src/main/java/cn/minbb/producttester/views/ScanDeviceActivity.java

@@ -0,0 +1,161 @@
+package cn.minbb.producttester.views;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
+import com.joanzapata.iconify.widget.IconTextView;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import cn.minbb.producttester.R;
+import cn.minbb.producttester.controls.FoldingLayout;
+import cn.minbb.producttester.controls.OnFoldListener;
+import cn.minbb.producttester.ctrl.App;
+
+public class ScanDeviceActivity extends AppCompatActivity {
+
+    @BindView(R.id.matchListLayout)
+    LinearLayout matchListLayout;
+    @BindView(R.id.searchListLayout)
+    LinearLayout searchListLayout;
+    @BindView(R.id.matchListArrow)
+    IconTextView matchListArrow;
+    @BindView(R.id.searchListArrow)
+    IconTextView searchListArrow;
+    @BindView(R.id.matchListFolding)
+    FoldingLayout matchListFolding;
+    @BindView(R.id.searchListFolding)
+    FoldingLayout searchListFolding;
+    @BindView(R.id.matchList)
+    ListView matchList;
+    @BindView(R.id.searchList)
+    ListView searchList;
+    @BindView(R.id.bottomLayout)
+    View bottomLayout;
+
+    private ProgressDialog dialog;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_scan_device);
+        ButterKnife.bind(this);
+
+        App.setupActionBar(this);
+    }
+
+    public static void start(Context context) {
+        Intent starter = new Intent(context, ScanDeviceActivity.class);
+        context.startActivity(starter);
+    }
+
+    private void showProgress(boolean show) {
+        if (dialog == null) {
+            dialog = new ProgressDialog(this);
+            dialog.setTitle("蓝牙串口");
+            dialog.setMessage("搜索设备中······");
+            dialog.setCancelable(false);
+        }
+        if (show) {
+            dialog.show();
+        } else {
+            dialog.dismiss();
+        }
+    }
+
+    private void handleAnimation(final View bar, final FoldingLayout foldingLayout, View parent, final View nextParent) {
+        foldingLayout.setFoldListener(new OnFoldListener() {
+            @Override
+            public void onStartFold(float foldFactor) {
+                bar.setClickable(true);
+                matchListArrow.setText("{fa-chevron-up}");
+                resetMarginToTop(foldingLayout, foldFactor, nextParent);
+            }
+
+            @Override
+            public void onFoldingState(float foldFactor, float foldDrawHeight) {
+                bar.setClickable(false);
+                resetMarginToTop(foldingLayout, foldFactor, nextParent);
+            }
+
+            @Override
+            public void onEndFold(float foldFactor) {
+                bar.setClickable(true);
+                matchListArrow.setText("{fa-chevron-up}");
+                resetMarginToTop(foldingLayout, foldFactor, nextParent);
+            }
+        });
+        animateFold(foldingLayout, 500);
+    }
+
+    private void resetMarginToTop(View view, float foldFactor, View nextParent) {
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) nextParent.getLayoutParams();
+        lp.topMargin = (int) (-view.getMeasuredHeight() * foldFactor) + dp2px(ScanDeviceActivity.this, 10);
+        nextParent.setLayoutParams(lp);
+    }
+
+    private void setMarginToTop(float foldFactor, View nextParent) {
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) nextParent.getLayoutParams();
+        lp.topMargin = (int) (-dp2px(ScanDeviceActivity.this, 135) * foldFactor) + dp2px(ScanDeviceActivity.this, 10);
+        nextParent.setLayoutParams(lp);
+    }
+
+    @SuppressLint("NewApi")
+    public void animateFold(FoldingLayout foldLayout, int duration) {
+        float foldFactor = foldLayout.getFoldFactor();
+        ObjectAnimator animator = ObjectAnimator.ofFloat(foldLayout, "foldFactor", foldFactor, foldFactor > 0 ? 0 : 1);
+        animator.setRepeatMode(ValueAnimator.REVERSE);
+        animator.setRepeatCount(0);
+        animator.setDuration(duration);
+        animator.setInterpolator(new AccelerateInterpolator());
+        animator.start();
+    }
+
+    public static int dp2px(Context context, float dpValue) {
+        float density = context.getResources().getDisplayMetrics().density;
+        return (int) (dpValue * density + 0.5f);
+    }
+
+    @OnClick({R.id.matchListToggle, R.id.searchListToggle})
+    public void onViewClicked(View view) {
+        switch (view.getId()) {
+            case R.id.matchListToggle:
+                handleAnimation(view, matchListFolding, matchListLayout, searchListLayout);
+                break;
+            case R.id.searchListToggle:
+                handleAnimation(view, searchListFolding, searchListLayout, bottomLayout);
+                break;
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        menu.add(0, 0, 0, "扫描").setIcon(R.drawable.ic_search_black_24dp).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                ScanDeviceActivity.this.finish();
+                break;
+            case 0:
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}

+ 9 - 0
app/src/main/res/drawable/ic_data_usage_black_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,2.05v3.03c3.39,0.49 6,3.39 6,6.92 0,0.9 -0.18,1.75 -0.48,2.54l2.6,1.53c0.56,-1.24 0.88,-2.62 0.88,-4.07 0,-5.18 -3.95,-9.45 -9,-9.95zM12,19c-3.87,0 -7,-3.13 -7,-7 0,-3.53 2.61,-6.43 6,-6.92V2.05c-5.06,0.5 -9,4.76 -9,9.95 0,5.52 4.47,10 9.99,10 3.31,0 6.24,-1.61 8.06,-4.09l-2.6,-1.53C16.17,17.98 14.21,19 12,19z"/>
+</vector>

+ 9 - 0
app/src/main/res/drawable/ic_search_black_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
+</vector>

+ 9 - 0
app/src/main/res/drawable/ic_storage_black_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,20h20v-4L2,16v4zM4,17h2v2L4,19v-2zM2,4v4h20L22,4L2,4zM6,7L4,7L4,5h2v2zM2,14h20v-4L2,10v4zM4,11h2v2L4,13v-2z"/>
+</vector>

+ 70 - 1
app/src/main/res/layout/activity_main.xml

@@ -7,6 +7,62 @@
     android:layout_height="match_parent"
     tools:context=".views.MainActivity">
 
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="0dp"
+        android:layout_height="?attr/actionBarSize"
+        android:background="?attr/colorPrimary"
+        android:minHeight="?attr/actionBarSize"
+        android:theme="?attr/actionBarTheme"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <android.support.constraint.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="@color/colorPrimary">
+
+            <com.joanzapata.iconify.widget.IconTextView
+                android:id="@+id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="{fa-server 20sp}"
+                android:textColor="#FFFFFF"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="8dp"
+                android:layout_marginLeft="8dp"
+                android:text="设备"
+                android:textColor="#FFFFFF"
+                android:textSize="18sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toEndOf="@+id/icon"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/device"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="8dp"
+                android:layout_marginRight="8dp"
+                android:text="无连接"
+                android:textColor="#FFFFFF"
+                android:textSize="14sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+        </android.support.constraint.ConstraintLayout>
+
+    </android.support.v7.widget.Toolbar>
+
     <android.support.v4.view.ViewPager
         android:id="@+id/viewPager"
         android:layout_width="0dp"
@@ -14,7 +70,7 @@
         app:layout_constraintBottom_toTopOf="@+id/navigation"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintTop_toBottomOf="@+id/toolbar" />
 
     <android.support.design.widget.BottomNavigationView
         android:id="@+id/navigation"
@@ -28,4 +84,17 @@
         app:layout_constraintRight_toRightOf="parent"
         app:menu="@menu/navigation" />
 
+    <com.joanzapata.iconify.widget.IconTextView
+        android:id="@+id/noDevice"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:gravity="center"
+        android:text="{fa-hand-pointer-o 64sp}\n\n点击连接设备"
+        android:textSize="20sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toTopOf="@+id/navigation"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/viewPager" />
+
 </android.support.constraint.ConstraintLayout>

+ 162 - 0
app/src/main/res/layout/activity_scan_device.xml

@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".views.ScanDeviceActivity">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:id="@+id/matchListLayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:layout_marginLeft="8dp"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginRight="8dp"
+            android:orientation="vertical">
+
+            <android.support.constraint.ConstraintLayout
+                android:id="@+id/matchListToggle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+
+                <com.joanzapata.iconify.widget.IconTextView
+                    android:id="@+id/matchListIcon"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginLeft="8dp"
+                    android:layout_marginTop="8dp"
+                    android:layout_marginBottom="8dp"
+                    android:text="{fa-link}"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginLeft="8dp"
+                    android:layout_marginTop="8dp"
+                    android:layout_marginBottom="8dp"
+                    android:text="已配对"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintStart_toEndOf="@+id/matchListIcon"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <com.joanzapata.iconify.widget.IconTextView
+                    android:id="@+id/matchListArrow"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="end"
+                    android:layout_marginTop="8dp"
+                    android:layout_marginBottom="8dp"
+                    android:text="{fa-chevron-down}"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+            </android.support.constraint.ConstraintLayout>
+
+            <cn.minbb.producttester.controls.FoldingLayout
+                android:id="@+id/matchListFolding"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:tag="main">
+
+                <ListView
+                    android:id="@+id/matchList"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" />
+
+            </cn.minbb.producttester.controls.FoldingLayout>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/searchListLayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:layout_marginLeft="8dp"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginRight="8dp"
+            android:orientation="vertical">
+
+            <android.support.constraint.ConstraintLayout
+                android:id="@+id/searchListToggle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+
+                <com.joanzapata.iconify.widget.IconTextView
+                    android:id="@+id/iconTextView2"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginLeft="8dp"
+                    android:layout_marginTop="8dp"
+                    android:layout_marginBottom="8dp"
+                    android:text="{fa-unlink}"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="8dp"
+                    android:layout_marginLeft="8dp"
+                    android:layout_marginTop="8dp"
+                    android:layout_marginBottom="8dp"
+                    android:text="搜索列表"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintStart_toEndOf="@+id/iconTextView2"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <com.joanzapata.iconify.widget.IconTextView
+                    android:id="@+id/searchListArrow"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="end"
+                    android:layout_marginTop="8dp"
+                    android:layout_marginBottom="8dp"
+                    android:text="{fa-chevron-down}"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+            </android.support.constraint.ConstraintLayout>
+
+            <cn.minbb.producttester.controls.FoldingLayout
+                android:id="@+id/searchListFolding"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:tag="main">
+
+                <ListView
+                    android:id="@+id/searchList"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" />
+
+            </cn.minbb.producttester.controls.FoldingLayout>
+
+        </LinearLayout>
+
+        <View
+            android:id="@+id/bottomLayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp" />
+
+    </LinearLayout>
+
+</ScrollView>

+ 1 - 32
app/src/main/res/layout/page_1.xml

@@ -1,38 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/page"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <android.support.v7.widget.Toolbar
-        android:id="@+id/toolbar"
-        android:layout_width="match_parent"
-        android:layout_height="?attr/actionBarSize"
-        android:background="@color/colorPrimary">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="?attr/actionBarSize"
-            android:background="@color/colorPrimary"
-            android:gravity="center_vertical"
-            android:orientation="horizontal">
-
-            <com.joanzapata.iconify.widget.IconTextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="{fa-home 20sp}"
-                android:textColor="#FFFFFF" />
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="4dp"
-                android:layout_marginLeft="4dp"
-                android:text=""
-                android:textColor="#FFFFFF"
-                android:textSize="18sp" />
-
-        </LinearLayout>
-
-    </android.support.v7.widget.Toolbar>
-
 </android.support.constraint.ConstraintLayout>

+ 0 - 32
app/src/main/res/layout/page_2.xml

@@ -3,36 +3,4 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <android.support.v7.widget.Toolbar
-        android:id="@+id/toolbar"
-        android:layout_width="match_parent"
-        android:layout_height="?attr/actionBarSize"
-        android:background="@color/colorPrimary">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="?attr/actionBarSize"
-            android:background="@color/colorPrimary"
-            android:gravity="center_vertical"
-            android:orientation="horizontal">
-
-            <com.joanzapata.iconify.widget.IconTextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="{fa-home 20sp}"
-                android:textColor="#FFFFFF" />
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="4dp"
-                android:layout_marginLeft="4dp"
-                android:text=""
-                android:textColor="#FFFFFF"
-                android:textSize="18sp" />
-
-        </LinearLayout>
-
-    </android.support.v7.widget.Toolbar>
-
 </android.support.constraint.ConstraintLayout>

+ 0 - 32
app/src/main/res/layout/page_3.xml

@@ -3,36 +3,4 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <android.support.v7.widget.Toolbar
-        android:id="@+id/toolbar"
-        android:layout_width="match_parent"
-        android:layout_height="?attr/actionBarSize"
-        android:background="@color/colorPrimary">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="?attr/actionBarSize"
-            android:background="@color/colorPrimary"
-            android:gravity="center_vertical"
-            android:orientation="horizontal">
-
-            <com.joanzapata.iconify.widget.IconTextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="{fa-home 20sp}"
-                android:textColor="#FFFFFF" />
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="4dp"
-                android:layout_marginLeft="4dp"
-                android:text=""
-                android:textColor="#FFFFFF"
-                android:textSize="18sp" />
-
-        </LinearLayout>
-
-    </android.support.v7.widget.Toolbar>
-
 </android.support.constraint.ConstraintLayout>

+ 6 - 6
app/src/main/res/menu/navigation.xml

@@ -3,17 +3,17 @@
 
     <item
         android:id="@+id/navigation_1"
-        android:icon="@drawable/ic_home_black_24dp"
-        android:title="1" />
+        android:icon="@drawable/ic_storage_black_24dp"
+        android:title="设备" />
 
     <item
         android:id="@+id/navigation_2"
-        android:icon="@drawable/ic_dashboard_black_24dp"
-        android:title="2" />
+        android:icon="@drawable/ic_data_usage_black_24dp"
+        android:title="数据" />
 
     <item
         android:id="@+id/navigation_3"
-        android:icon="@drawable/ic_notifications_black_24dp"
-        android:title="3" />
+        android:icon="@drawable/ic_dashboard_black_24dp"
+        android:title="扩展" />
 
 </menu>

+ 4 - 0
app/src/main/res/values/attrs.xml

@@ -9,4 +9,8 @@
         <attr name="metaButtonBarButtonStyle" format="reference" />
     </declare-styleable>
 
+    <declare-styleable name="FoldingMenu">
+        <attr name="foldNumber" format="integer" />
+    </declare-styleable>
+
 </resources>