我创建了一个名为StripedLayout的自定义ViewGroup
/**
* A Layout that lays out child view horrizontally and makes them
* take up a prescribed % of the width of the screen .
*/
@SuppressWarnings("CanBeFinal")
@RemoteViews.RemoteView
public class StripedLayout extends ViewGroup
{
private static final String TAG ="StripedLayout";
/** The width we want to set for each child element as a proportion of the screen width*/
private ArrayList<Integer> mSectionsFromLeftToRightPercent;
private Integer mDividerWidth = 16;
/**for drawing dividing lines between screen elements*/
private Paint mPaint;
/**For holding information about the display*/
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
/**The gravity that we will use equivalent to xml android:layout_gravity="fill_vertical|center_horizontal"*/
private static final Integer GRAVITY = 113;
/** These are used for computing child frames based on their gravity. */
private final Rect mTmpContainerRect = new Rect();
private final Rect mTmpChildRect = new Rect();
public StripedLayout(Context context)
{
super(context);
setWillNotDraw(false);
setVerticalScrollBarEnabled(true);
getPaint();
setScrollContainer(true);
mSectionsFromLeftToRightPercent = new ArrayList<>();
}
public StripedLayout(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
setWillNotDraw(false);
setVerticalScrollBarEnabled(true);
getPaint();
mSectionsFromLeftToRightPercent = new ArrayList<>();
}
public StripedLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setWillNotDraw(false);
setVerticalScrollBarEnabled(true);
getPaint();
mSectionsFromLeftToRightPercent = new ArrayList<>();
Integer dividerWidth = mDividerWidth;
/*get Layout Params*/
// Pull the layout param values from the layout XML during
// inflation. This is not needed if you don't care about
// changing the layout behavior in XML.
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StripedLayout );
dividerWidth = a.getDimensionPixelSize(R.styleable.StripedLayout_divider_width, dividerWidth);
mDividerWidth = dividerWidth;
Integer paintColour = a.getColor(R.styleable.StripedLayout_divider_colour, getResources().getColor(android.R.color.black));
getPaint(paintColour);
a.recycle();
}
/** get the default paint colour**/
private void getPaint()
{
//Paint the divider the default colour but do this only once
if(mPaint == null)
{mPaint = new Paint(getResources().getColor(android.R.color.black));}
}
//Change the paint from the default colour to another colour
private void getPaint(Integer colour)
{mPaint.setColor(colour);}
/**
* Any layout manager that doesn't scroll will want this.
*/
@Override
public boolean shouldDelayChildPressedState()
{return false;}
/**
* Ask all children to measure themselves and compute the measurement of this
* layout based on the children.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
//get the display height and width
Display display = getDisplay();
display.getMetrics(mDisplayMetrics);
int widthInPixels = mDisplayMetrics.widthPixels;
//get the height of the screen
Integer screenHeight = mDisplayMetrics.heightPixels;
// Report our final dimensions.
setMeasuredDimension(widthInPixels, screenHeight);
Log.d(TAG, "onMeasure");
//get Attributes
Integer screenUsedPercentage =0;
final int childCount = getChildCount();
for (int childNumber = 0; childNumber < childCount; childNumber++)
{
final View child = getChildAt(childNumber);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Integer previousPercentageInPixels = 0;
if(childNumber >0)
{previousPercentageInPixels = mSectionsFromLeftToRightPercent.get(childNumber -1);}
//Determine the size for each segment
Integer nextPercentageInPixels = (widthInPixels /100) * lp.percentage;
//keep use of the percentage of the screen that is being used
screenUsedPercentage += lp.percentage;
Integer nextScreenPercentage = previousPercentageInPixels + nextPercentageInPixels;
mSectionsFromLeftToRightPercent.add(childNumber,nextScreenPercentage);
//round up the last measurement so that the last divider is not displayed
if(screenUsedPercentage >= 95)
{mSectionsFromLeftToRightPercent.add(childNumber,widthInPixels);}
}
}
/**
* Draws vertical lines on the canvas in order to partition
* the canvas into sections
* NOTE Each line exists on the left hand side of each section and
* takes up room on the left hand side
* @param canvas the canvas on which the dividers are drawn
*/
@Override
protected void onDraw(Canvas canvas)
{
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = getChildAt(i);
if (child != null && child.getVisibility() != GONE)
{
Integer left = mSectionsFromLeftToRightPercent.get(i);
Integer right = left + mDividerWidth;
Integer top = getPaddingTop();
Integer bottom = getHeight();
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
Log.d(TAG, "onDraw");
}
/**
* Position all children within this layout.
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
final int childCount = getChildCount();
Integer currentLeftBoundary =0;
for (int childNumber = 0; childNumber < childCount; childNumber++)
{
final View child = getChildAt(childNumber);
if (child.getVisibility() != GONE)
{
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = mSectionsFromLeftToRightPercent.get(childNumber) - currentLeftBoundary;
final int height = child.getMeasuredHeight();
//Measure the child so that it takes up all the room required
child.measure(width, height);
child.computeScroll();
child.setScrollContainer(true);
//allow space for divider
child.setPadding(Math.max(child.getPaddingLeft(), mDividerWidth) ,child.getPaddingTop(),Math.max(child.getPaddingRight(), mDividerWidth),child.getPaddingBottom());
// Compute the frame in which we are placing this child.
mTmpContainerRect.left = currentLeftBoundary + getLeftMargin(lp.leftMargin, childNumber);
mTmpContainerRect.right = mSectionsFromLeftToRightPercent.get(childNumber); //getRightMargin(lp.rightMargin, childNumber, childCount );
//fill the height of the screen
mTmpContainerRect.top = top + lp.topMargin;
mTmpContainerRect.bottom = bottom - lp.bottomMargin;
//move the left boundary to the right for next time
currentLeftBoundary = mSectionsFromLeftToRightPercent.get(childNumber);
// Use the child's gravity and size to determine its final
// frame within its container.
Gravity.apply(GRAVITY, width, height, mTmpContainerRect, mTmpChildRect);
// Place the child.
child.layout(mTmpChildRect.left, mTmpChildRect.top, mTmpChildRect.right, mTmpChildRect.bottom);
}
}
}
/**A larger margin is required on the left hand side to account for the fact that the vertical dividing line graphic is wholly within the
* left side of the child element and should not be concealed by the child element. The first child does not have such graphic to it's left so
* it will not need the extra wide left margin.
* @param margin the standard margin size
* @param childNumber Which child is being processed
* @return integer relating to left margin width in pixels
*/
private Integer getLeftMargin(Integer margin, Integer childNumber)
{
//Only a normal size margin is required for the first child due to there being no graphic to the left
if(childNumber==0)
{return margin;}
//We need an extra wide left margin in order to display the line divider graphics
return margin + mDividerWidth*2;
}
// ----------------------------------------------------------------------
// The rest of the implementation is for custom per-child layout parameters.
// If you do not need these (for example you are writing a layout manager
// that does fixed positioning of its children), you can drop all of this.
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
/**
* Custom per-child layout information.
*/
private static class LayoutParams extends MarginLayoutParams {
/**
* The gravity to apply with the View to which these layout parameters
* are associated.
*/
private int gravity = Gravity.TOP | Gravity.START;
private int position = 0;
private int percentage = 33;
private LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
// Pull the layout param values from the layout XML during
// inflation. This is not needed if you don't care about
// changing the layout behavior in XML.
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StripedLayout_Layout );
gravity = a.getInt(R.styleable.StripedLayout_Layout_android_gravity, gravity);
position = a.getInt(R.styleable.StripedLayout_Layout_position, position);
percentage = a.getInt(R.styleable.StripedLayout_Layout_percentage, percentage);
a.recycle();
}
private LayoutParams(int width, int height) {
super(width, height);
}
private LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}
此xml的活动使用布局
<com.blackstephen.line_wizard.common.utility.StripedLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.blackstephen.line_wizard.show.view.ActivityBoatAndLine"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:divider_width = "8dp"
app:divider_colour= "@android:color/black">
<FrameLayout
android:id="@+id/boat_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:percentage="40">
</FrameLayout>
<FrameLayout
android:id="@+id/line_purpose_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:percentage="20">
</FrameLayout>
<FrameLayout
android:id="@+id/line_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:percentage="40">
</FrameLayout>
</com.blackstephen.line_wizard.common.utility.StripedLayout>
然后使用片段事务添加这些片段
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/light_theme_fragment"
tools:context="com.blackstephen.line_wizard.show.view.FragmentSailBoat">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/light_theme_fragment_toolbar"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_new_boat"
style="?android:attr/borderlessButtonStyle"
android:id="@+id/btn_new_boat"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_view_shopping_list"
style="?android:attr/borderlessButtonStyle"
android:id="@+id/btn_view_shopping_list"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_recent_purchases"
android:textAlignment="textStart"
style="?android:attr/borderlessButtonStyle"
android:background="@color/light_theme_fragment_toolbar"
android:text="@string/button_recent_purchases"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/sail_boats_heading"
tools:ignore="UnusedAttribute"/>
<ViewSwitcher
android:id="@+id/data_status_view_switcher_boat"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="@string/view_switcher_recycler_view"
android:id="@+id/sail_boat_recycler_view">
</android.support.v7.widget.RecyclerView>
<include layout="@layout/empty_item_layout"/>
</ViewSwitcher>
</LinearLayout>
但是问题是,RecyclerView放置在StripedLayout中时将不会滚动。有人可以帮忙吗?