Android 사용자 정의 View 수직 시간 축 레이아웃 구현
분석 하 다.
이 를 실현 하 는 가장 자주 사용 하 는 방법 은 바로 ListView 입 니 다.저 는 LinearLayout 를 계승 하 는 방식 으로 이 루어 집 니 다.우선 사용자 정의 속성 을 정의 합 니 다:
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TimelineLayout">
<!-- -->
<attr name="line_margin_left" format="dimension"/>
<!-- -->
<attr name="line_margin_top" format="dimension"/>
<!-- -->
<attr name="line_stroke_width" format="dimension"/>
<!-- -->
<attr name="line_color" format="color"/>
<!-- -->
<attr name="point_size" format="dimension"/>
<!-- -->
<attr name="point_color" format="color"/>
<!-- -->
<attr name="icon_src" format="reference"/>
</declare-styleable>
</resources>
TimelineLayout.java
package com.jackie.timeline;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
/**
* Created by Jackie on 2017/3/8.
*
*/
public class TimelineLayout extends LinearLayout {
private Context mContext;
private int mLineMarginLeft;
private int mLineMarginTop;
private int mLineStrokeWidth;
private int mLineColor;;
private int mPointSize;
private int mPointColor;
private Bitmap mIcon;
private Paint mLinePaint; //
private Paint mPointPaint; //
//
private int mFirstX;
private int mFirstY;
//
private int mLastX;
private int mLastY;
public TimelineLayout(Context context) {
this(context, null);
}
public TimelineLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public TimelineLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimelineLayout);
mLineMarginLeft = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_line_margin_left, 10);
mLineMarginTop = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_line_margin_top, 0);
mLineStrokeWidth = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_line_stroke_width, 2);
mLineColor = ta.getColor(R.styleable.TimelineLayout_line_color, 0xff3dd1a5);
mPointSize = ta.getDimensionPixelSize(R.styleable.TimelineLayout_point_size, 8);
mPointColor = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_point_color, 0xff3dd1a5);
int iconRes = ta.getResourceId(R.styleable.TimelineLayout_icon_src, R.drawable.ic_ok);
BitmapDrawable drawable = (BitmapDrawable) context.getResources().getDrawable(iconRes);
if (drawable != null) {
mIcon = drawable.getBitmap();
}
ta.recycle();
setWillNotDraw(false);
initView(context);
}
private void initView(Context context) {
this.mContext = context;
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setDither(true);
mLinePaint.setColor(mLineColor);
mLinePaint.setStrokeWidth(mLineStrokeWidth);
mLinePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPointPaint = new Paint();
mPointPaint.setAntiAlias(true);
mPointPaint.setDither(true);
mPointPaint.setColor(mPointColor);
mPointPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawTimeline(canvas);
}
private void drawTimeline(Canvas canvas) {
int childCount = getChildCount();
if (childCount > 0) {
if (childCount > 1) {
// 1, 2 , , icon
drawFirstPoint(canvas);
drawLastIcon(canvas);
drawBetweenLine(canvas);
} else if (childCount == 1) {
drawFirstPoint(canvas);
}
}
}
private void drawFirstPoint(Canvas canvas) {
View child = getChildAt(0);
if (child != null) {
int top = child.getTop();
mFirstX = mLineMarginLeft;
mFirstY = top + child.getPaddingTop() + mLineMarginTop;
//
canvas.drawCircle(mFirstX, mFirstY, mPointSize, mPointPaint);
}
}
private void drawLastIcon(Canvas canvas) {
View child = getChildAt(getChildCount() - 1);
if (child != null) {
int top = child.getTop();
mLastX = mLineMarginLeft;
mLastY = top + child.getPaddingTop() + mLineMarginTop;
//
canvas.drawBitmap(mIcon, mLastX - (mIcon.getWidth() >> 1), mLastY, null);
}
}
private void drawBetweenLine(Canvas canvas) {
// ,
canvas.drawLine(mFirstX, mFirstY, mLastX, mLastY, mLinePaint);
for (int i = 0; i < getChildCount() - 1; i++) {
//
int top = getChildAt(i).getTop();
int y = top + getChildAt(i).getPaddingTop() + mLineMarginTop;
canvas.drawCircle(mFirstX, y, mPointSize, mPointPaint);
}
}
public int getLineMarginLeft() {
return mLineMarginLeft;
}
public void setLineMarginLeft(int lineMarginLeft) {
this.mLineMarginLeft = lineMarginLeft;
invalidate();
}
}
위의 코드 를 통 해 알 수 있 듯 이 세 단계 로 나 누 어 그립 니 다.먼저 시작 하 는 실심 원 을 그립 니 다.그리고 끝 나 는 아이콘 을 그립 니 다.그리고 시작 과 끝 사이 에 선 을 그립 니 다.그리고 온라인 에서 모든 단계 의 실심 원 을 그립 니 다.activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:weightSum="2">
<Button
android:id="@+id/add_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="add"/>
<Button
android:id="@+id/sub_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="sub"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<Button
android:id="@+id/add_margin"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="+"/>
<Button
android:id="@+id/sub_margin"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="-"/>
</LinearLayout>
<TextView
android:id="@+id/current_margin"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:text="current line margin left is 25dp"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<com.jackie.timeline.TimelineLayout
android:id="@+id/timeline_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:line_margin_left="25dp"
app:line_margin_top="8dp"
android:orientation="vertical"
android:background="@android:color/white">
</com.jackie.timeline.TimelineLayout>
</ScrollView>
</LinearLayout>
MainActivity.java
package com.jackie.timeline;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button addItemButton;
private Button subItemButton;
private Button addMarginButton;
private Button subMarginButton;
private TextView mCurrentMargin;
private TimelineLayout mTimelineLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
addItemButton = (Button) findViewById(R.id.add_item);
subItemButton = (Button) findViewById(R.id.sub_item);
addMarginButton= (Button) findViewById(R.id.add_margin);
subMarginButton= (Button) findViewById(R.id.sub_margin);
mCurrentMargin= (TextView) findViewById(R.id.current_margin);
mTimelineLayout = (TimelineLayout) findViewById(R.id.timeline_layout);
addItemButton.setOnClickListener(this);
subItemButton.setOnClickListener(this);
addMarginButton.setOnClickListener(this);
subMarginButton.setOnClickListener(this);
}
private int index = 0;
private void addItem() {
View view = LayoutInflater.from(this).inflate(R.layout.item_timeline, mTimelineLayout, false);
((TextView) view.findViewById(R.id.tv_action)).setText(" " + index);
((TextView) view.findViewById(R.id.tv_action_time)).setText("2017 3 8 16:55:04");
((TextView) view.findViewById(R.id.tv_action_status)).setText(" ");
mTimelineLayout.addView(view);
index++;
}
private void subItem() {
if (mTimelineLayout.getChildCount() > 0) {
mTimelineLayout.removeViews(mTimelineLayout.getChildCount() - 1, 1);
index--;
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.add_item:
addItem();
break;
case R.id.sub_item:
subItem();
break;
case R.id.add_margin:
int currentMargin = UIHelper.pxToDip(this, mTimelineLayout.getLineMarginLeft());
mTimelineLayout.setLineMarginLeft(UIHelper.dipToPx(this, ++currentMargin));
mCurrentMargin.setText("current line margin left is " + currentMargin + "dp");
break;
case R.id.sub_margin:
currentMargin = UIHelper.pxToDip(this, mTimelineLayout.getLineMarginLeft());
mTimelineLayout.setLineMarginLeft(UIHelper.dipToPx(this, --currentMargin));
mCurrentMargin.setText("current line margin left is " + currentMargin + "dp");
break;
default:
break;
}
}
}
item_timeline.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="65dp"
android:paddingTop="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp">
<TextView
android:id="@+id/tv_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#1a1a1a"
android:text=" "/>
<TextView
android:id="@+id/tv_action_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="#8e8e8e"
android:layout_below="@id/tv_action"
android:layout_marginTop="10dp"
android:text="2017 3 8 16:49:12"/>
<TextView
android:id="@+id/tv_action_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#3dd1a5"
android:layout_alignParentRight="true"
android:text=" "/>
</RelativeLayout>
픽 셀 도구 변환 도구 종류 첨부:
package com.jackie.timeline;
import android.content.Context;
/**
* Created by Jackie on 2017/3/8.
*/
public final class UIHelper {
private UIHelper() throws InstantiationException {
throw new InstantiationException("This class is not for instantiation");
}
/**
* dip px
*/
public static int dipToPx(Context context, float dip) {
return (int) (dip * context.getResources().getDisplayMetrics().density + 0.5f);
}
/**
* px dip
*/
public static int pxToDip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
효과 도 는 다음 과 같다.이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.