Android Open Source - cwac-masterdetail Split Pane Layout






From Project

Back to project page cwac-masterdetail.

License

The source code is released under:

Apache License

If you think the Android project cwac-masterdetail listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 *  Android Split Pane Layout./*w  w  w . j  a  v a 2  s . c om*/
 *  https://github.com/MobiDevelop/android-split-pane-layout
 *  
 *  Copyright (C) 2012 Justin Shapcott
 *  Minor tweaks (c) 2013 CommonsWare, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.mobidevelop.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.ViewGroup;


/**
 * A layout that splits the available space between two child views.
 * 
 * An optionally movable bar exists between the children which allows the user
 * to redistribute the space allocated to each view.
 */
public class SplitPaneLayout extends ViewGroup {
  public static final int ORIENTATION_HORIZONTAL = 0;
  public static final int ORIENTATION_VERTICAL = 1;
  
  private int mOrientation = 0;
  private int mSplitterSize = 12;
  private boolean mSplitterMovable = true;
  private int mSplitterPosition = Integer.MIN_VALUE;
  private float mSplitterPositionPercent = 0.5f;
  
  private Drawable mSplitterDrawable;
  private Drawable mSplitterDraggingDrawable;
  
  private Rect mSplitterRect = new Rect();
  
  private int lastX;
  private int lastY;
  private Rect temp = new Rect();
  private boolean isDragging = false;
  
  public SplitPaneLayout(Context context) {
    super(context);
    mSplitterPositionPercent = 0.5f;
    mSplitterDrawable = new PaintDrawable(0x88FFFFFF);
    mSplitterDraggingDrawable = new PaintDrawable(0x88FFFFFF);
  } 
  public SplitPaneLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    extractAttributes(context, attrs);
  }
  public SplitPaneLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    extractAttributes(context, attrs);
  }
  
  private void extractAttributes(Context context, AttributeSet attrs) {
    // removed to avoid needing a library project
  }
  
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
     
    check();

    if (widthSize > 0 && heightSize > 0) {
      switch (mOrientation) {
        case 0: {
          if (mSplitterPosition == Integer.MIN_VALUE && mSplitterPositionPercent < 0) {
            mSplitterPosition = widthSize / 2;
          } else if (mSplitterPosition == Integer.MIN_VALUE && mSplitterPositionPercent >= 0) {
            mSplitterPosition = (int) (widthSize * mSplitterPositionPercent);
          } else if (mSplitterPosition != Integer.MIN_VALUE && mSplitterPositionPercent < 0) {
            mSplitterPositionPercent = (float) mSplitterPosition / (float) widthSize;
          }
          getChildAt(0).measure(MeasureSpec.makeMeasureSpec(mSplitterPosition - (mSplitterSize / 2), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));
          getChildAt(1).measure(MeasureSpec.makeMeasureSpec(widthSize - (mSplitterSize / 2) - mSplitterPosition, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));       
          break;
        }
        case 1: {
          if (mSplitterPosition == Integer.MIN_VALUE && mSplitterPositionPercent < 0) {
            mSplitterPosition = heightSize / 2;
          } else if (mSplitterPosition == Integer.MIN_VALUE && mSplitterPositionPercent >= 0) {
            mSplitterPosition = (int) (heightSize * mSplitterPositionPercent);
          } else if (mSplitterPosition != Integer.MIN_VALUE && mSplitterPositionPercent < 0) {
            mSplitterPositionPercent = (float) mSplitterPosition / (float) heightSize;
          }     
          getChildAt(0).measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mSplitterPosition - (mSplitterSize / 2), MeasureSpec.EXACTLY));
          getChildAt(1).measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize - (mSplitterSize / 2) - mSplitterPosition, MeasureSpec.EXACTLY));
          break;
        }
      }   
    }
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int w = r - l;
    int h = b - t;
    switch (mOrientation) {
      case 0: {
        getChildAt(0).layout(0, 0, mSplitterPosition - (mSplitterSize / 2), h);         
        mSplitterRect.set(mSplitterPosition - (mSplitterSize / 2), 0, mSplitterPosition + (mSplitterSize / 2), h);          
        getChildAt(1).layout(mSplitterPosition + (mSplitterSize / 2), 0, r, h);
        break;
      }
      case 1: {
        getChildAt(0).layout(0, 0, w, mSplitterPosition - (mSplitterSize / 2));         
        mSplitterRect.set(0, mSplitterPosition - (mSplitterSize / 2), w, mSplitterPosition + (mSplitterSize / 2));          
        getChildAt(1).layout(0, mSplitterPosition + (mSplitterSize / 2), w, h);
        break;
      }
    }
  }
  
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (mSplitterMovable) {
      int x = (int) event.getX();
      int y = (int) event.getY();

      switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
          if (mSplitterRect.contains(x,y)) {
            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
            isDragging = true;
            temp.set(mSplitterRect);
            invalidate(temp);
            lastX = x;
            lastY = y;          
          }       
          break;
        }
        case MotionEvent.ACTION_MOVE: {
          if (isDragging) {         
            switch (mOrientation) {
              case ORIENTATION_HORIZONTAL: {
                temp.offset((x - lastX), 0);
                break;
              }
              case ORIENTATION_VERTICAL: {
                temp.offset(0, (int) (y - lastY));
                break;
              }
            }
            lastX = x;
            lastY = y;
            invalidate();
          }     
          break;
        }
        case MotionEvent.ACTION_UP: {
          if (isDragging) {
            isDragging = false;
            switch (mOrientation) {
              case ORIENTATION_HORIZONTAL: {
                mSplitterPosition = x;
                break;
              }
              case ORIENTATION_VERTICAL: {
                mSplitterPosition = y;
                break;
              }           
            }
            mSplitterPositionPercent = -1;
            remeasure();
            requestLayout();
          }
          break;
        }
      }
      return true;
    }
    return false;
  }
  
  @Override
  protected Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState ss = new SavedState(superState);
    ss.mSplitterPositionPercent = mSplitterPositionPercent;   
    return ss;
  }
  @Override
  protected void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof SavedState)) {
      super.onRestoreInstanceState(state);
      return;
    }
    SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());
        setSplitterPositionPercent(ss.mSplitterPositionPercent);
  } 

  /**
   * Convenience for calling own measure method.
   */
  private void remeasure() {
    measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
        MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)
    );
  }
  
  /**
   * Checks that we have exactly two children.
   */ 
  private void check() {
    if (getChildCount() != 2) {
      throw new RuntimeException("SplitPaneLayout must have exactly two child views.");
    }
  }
  
  @Override
  protected void dispatchDraw(Canvas canvas) {    
    super.dispatchDraw(canvas);
    if (mSplitterDrawable != null) {
      mSplitterDrawable.setBounds(mSplitterRect);
      mSplitterDrawable.draw(canvas);
    } 
    if (isDragging) {
      mSplitterDraggingDrawable.setBounds(temp);
      mSplitterDraggingDrawable.draw(canvas);
    }
  }
  
  /**
   * Gets the current drawable used for the splitter.
   * 
   * @return the drawable used for the splitter
   */     
  public Drawable getSplitterDrawable() {
    return mSplitterDrawable;
  }
  
  /**
   * Sets the drawable used for the splitter.
   * 
   * @param splitterDrawable the desired orientation of the layout
   */   
  public void setSplitterDrawable(Drawable splitterDrawable) {
    mSplitterDrawable = splitterDrawable;
    if (getChildCount() == 2) {
      remeasure();  
    }
  }
  
  /**
   * Gets the current drawable used for the splitter dragging overlay.
   * 
   * @return the drawable used for the splitter
   */   
  public Drawable getSplitterDraggingDrawable() {
    return mSplitterDraggingDrawable;
  }
  
  /**
   * Sets the drawable used for the splitter dragging overlay.
   * 
   * @param splitterDrawable the desired orientation of the layout
   */   
  public void setSplitterDraggingDrawable(Drawable splitterDraggingDrawable) {
    mSplitterDraggingDrawable = splitterDraggingDrawable;
    if (isDragging) {
      invalidate();
    }
  }
  
  /**
   * Gets the current orientation of the layout.
   * 
   * @return the orientation of the layout
   */   
  public int getOrientation() {
    return mOrientation;
  }
  
  /**
   * Sets the orientation of the layout.
   * 
   * @param orientation the desired orientation of the layout
   */   
  public void setOrientation(int orientation) {
    if (mOrientation != orientation) {
      mOrientation = orientation;
      if (getChildCount() == 2) {
        remeasure();  
      }     
    }   
  }
  
  /**
   * Gets the current size of the splitter in pixels.
   * 
   * @return the size of the splitter
   */ 
  public int getSplitterSize() {
    return mSplitterSize; 
  }
  
  /**
   * Sets the current size of the splitter in pixels.
   * 
   * @param splitterSize the desired size of the splitter
   */   
  public void setSplitterSize(int splitterSize) {
    mSplitterSize = splitterSize;
    if (getChildCount() == 2) {
      remeasure();  
    }   
  }
  
  /**
   * Gets whether the splitter is movable by the user.
   * 
   * @return whether the splitter is movable
   */   
  public boolean isSplitterMovable() {
    return mSplitterMovable;
  }
  
  /**
   * Sets whether the splitter is movable by the user.
   * 
   * @param splitterMovable whether the splitter is movable
   */   
  public void setSplitterMovable(boolean splitterMovable) {
    mSplitterMovable = splitterMovable;
  }
  
  /**
   * Gets the current position of the splitter in pixels.
   * 
   * @return the position of the splitter
   */
  public int getSplitterPosition() {
    return mSplitterPosition;
  }
  /**
   * Sets the current position of the splitter in pixels.
   * 
   * @param position the desired position of the splitter
   */ 
  public void setSplitterPosition(int position) {
    if (position < 0) {
      position = 0;
    }
    mSplitterPosition = position;
    mSplitterPositionPercent = -1;
    remeasure();
  }
  
  /**
   * Gets the current position of the splitter as a percent.
   * 
   * @return the position of the splitter
   */
  public float getSplitterPositionPercent() {
    return mSplitterPositionPercent;
  } 
  
  /**
   * Sets the current position of the splitter as a percentage of the layout.
   * 
   * @param position the desired position of the splitter
   */   
  public void setSplitterPositionPercent(float position) {
    if (position < 0) {
      position = 0;
    }
    if (position > 1) {
      position = 1;
    }
    mSplitterPosition = Integer.MIN_VALUE;
    mSplitterPositionPercent = position;
    remeasure();
  }

  
  /**
   * Holds important values when we need to save instance state.
   */
  public static class SavedState extends BaseSavedState {
    float mSplitterPositionPercent;
    
    SavedState(Parcelable superState) {
      super(superState);
    }   
    @Override
        public void writeToParcel(Parcel out, int flags) {
      super.writeToParcel(out, flags);
      out.writeFloat(mSplitterPositionPercent);
    }
        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
        private SavedState(Parcel in) {
            super(in);
            mSplitterPositionPercent = in.readFloat();
        }        
  }
}




Java Source Code List

com.commonsware.cwac.masterdetail.MasterDetailActivity.java
com.commonsware.cwac.masterdetail.MasterDetailController.java
com.commonsware.cwac.masterdetail.MasterDetailHelper.java
com.commonsware.cwac.masterdetail.MasterDetailOptions.java
com.commonsware.cwac.masterdetail.MasterDetailStrategy.java
com.commonsware.cwac.masterdetail.PagerListAdapter.java
com.commonsware.cwac.masterdetail.demo.ContractFragment.java
com.commonsware.cwac.masterdetail.demo.EditorFragment.java
com.commonsware.cwac.masterdetail.demo.MainActivity.java
com.commonsware.cwac.masterdetail.demo.NoteHelper.java
com.commonsware.cwac.masterdetail.demo.Note.java
com.mobidevelop.widget.SplitPaneLayout.java