自定义View
自定义View
园林美的概念及特征
• 园林美的特征
– 园林中的自然美
• 植物、大自然的山川草木、风云雨雪、日月星辰、虫鱼鸟兽以及大自然晦明、阴晴、晨昏、 昼夜、春秋的瞬息变化、声音美等等。
– 园林中的生活美
• 首先应该使园林的空气清新,无污染,水体清透无异味,卫生条件良好; • 第二,要有宜人的小气候,使气温、湿度、风等综合作用达到理想的要求。 • 第三要避免噪音。要避免噪音的干扰就要在规划时深入研究场地环境,根据具体情况设置防
景
•景
– 是以自然物为主体所形成的,能引起美感的审美 对象,而且必定是以时空为特点(景的最佳观赏 时间、空间)的多维空间,具有诗情画意,令人 赏新悦目,使人流连。
• 园林中的景
– 是指在园林绿地中,自然或经人工创造的,以能 引起人的美感为特征的一种供作游憩观赏的空间 环境。
• 杭州西湖十景(断桥残雪、苏堤春晓、平湖秋月、三 潭映月、柳浪闻莺、雷峰夕照、曲院风荷、双峰插云、 花港观鱼、南屏晚钟)、燕京八景、圆明园四十景、 避暑山庄七十二、连续的、逐渐 的变化。
• 例如自然界中一年四季的季相变化;天穹中自天空到 地平线的色彩变化;人的视野由近到远,物体从清晰 到模糊的过程,建筑墙面由于光源影响所呈现的由明 到暗以及色彩上逐渐的转变等均属之。
图3-4 韵律与节奏
园林构图的基本规律
• 多样与统一
护林或采取消音和隔音的处理。 • 第四植物种类要丰富,生长健壮繁茂,形成立体景观。 • 第五要有方便的交通,完善的生活福利设施,适合园林的文化娱乐活动和美丽安静的休息环
境。
– 园林中的艺术美
桂林山水甲天下
园林构图的基本规律
• 园林绿地构图的含义
– 在一定的空间内,结合各种园林绿地的功能要 求对各种构景要素的取舍、剪裁、配布以及组 合称为园林绿地艺术构图。
【IT专家】实现淘宝订单(比如订单生成、未支付的订单等等)自定义view的实现
本文由我司收集整编,推荐下载,如有疑问,请与我司联系实现淘宝订单(比如订单生成、未支付的订单等等)自定义view 的实现2016/06/23 3231 这段时间做的三个app 会经常遇到自定义listview 进行动态的添加商品然后进行结算,在加入到购物车进行结算,结算的方式就是老三样。
首先实现的思路就是在主视图(比如fragment、activity 或者是fragmentactivity)中有个被scrollview 嵌套的linearlayout 去addview 自定义的listview,那自定义的listview是继承于linearlayout,创建完之后自身在加一下,创建view 就是这样,然后通过遍历集合数据进行动态的添加到主视图的linearlayout 中去。
代码的实现如下所示://这是自己定义的listview 就是动态添加数据的view private TextView tv_gotogive; public OrderListview(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub this.context = context; init(); public OrderListview(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub this.context = context; init(); public OrderListview(Context context) { super(context); // TODO Auto-generated constructor stub this.context = context; init(); private void init() { // TODO Auto-generated method stub afinal = FinalBitmap.create(context); LayoutInflater inflater = (LayoutInflater) context.getSystemService(YOUT_INFLATER_SERVICE); View view = inflater.inflate(yout.orderlistview, null); listView=(ListView) view.findViewById(R.id.ll_order_list); //头部tv_time_date = (TextView) view.findViewById(_time_date); tv_time_hours = (TextView) view.findViewById(_time_hours); //底部tv_totalprice = (TextView) view.findViewById(_totalprice); tv_gotogive = (TextView) view.findViewById(_gotogive); tv_gotogive.setOnClickListener(new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub System.out.println(“void”);this.addView(view, new。
OC 自定义视图
绘制色彩
在Red Square中,我们是使用NSColor类的set 方法来指定采用红色绘制后面的内容。但是,这 种方式同时隐藏了Cocoa中颜色是如何使用的。 一个NSColor对象描绘了由多个组件组成的一种 颜色以及某些时候的不透明性(相对的称为透明 度、透明)。这些组件可以存在于下列多种颜色 空间中:(一个NSColor对象描绘了由多种色彩 元素(在某些时候还有透明度)组成的一种颜色。 这些色彩元素可以存在于下列多种颜色空间中:)
绘制色彩
* Device RGB(设备RGB),主要的成 分(色彩元素)是由设备生成的红色,绿 色,蓝色。因为显示器,打印机以及其他 的输出设备的运作方式不同,在这个空间 内定义的颜色会随设备的不同而不同。
绘制色彩
* Calibrated RGB(校准RGB),主要的成 分(色彩元素)是红色,绿色,蓝色。它 们都在一个一致的,抽象的颜色空间中的, 因此这些颜色可以在其他任意的输出设备 中以同样的或者相近的颜色显示。对于大 部分工作,推荐使用这种颜色空间。
在同一时刻,只有一个视图能拥有焦点, 如果调用lockFocus方法时,焦点已经被锁 定在其他视图上了,那么这个视图的锁定 将会被压入栈。所以,在当前视图发送 unlockFocus消息释放锁定后,焦点可以被 恢复到先前的状态。
在视图中绘制字符串
我们将创建一个程序,这个程序可以在自 定义的NSView子类中绘制字符串。在 Project Builder中,创建一个新的名 为”String View”的Cocoa应用程序工程 (File > New Project > Application > Cocoa Application),并将它保存在 ~/LearningCocoa 文件夹中。
DataGridView自定义控件类
DataGridView⾃定义控件类1public class clsWindowGird2 {3public static void Editcol(int iColumnCount, DataGridView Grid, string sCaption, string sName, int iWidth, bool bVisible, bool bEdit, DataGridViewColumnSortMode SortMode, GridColumnType ColumnType)4 {5 DataGridViewColumn column = new DataGridViewColumn();6switch (ColumnType)7 {8case GridColumnType.enCheckBox:9 column = new DataGridViewCheckBoxColumn();10 column.HeaderCell = new DataGridViewCheckBoxHeaderCell();11break;12case GridColumnType.enTextBox:13 column = new DataGridViewTextBoxColumn();//new DataGridViewTextBoxColumn();14break;15case GridColumnType.enComboBox:16 column = new DataGridViewComboBoxColumn();17break;18case GridColumnType.enButton:19 column = new DataGridViewDisableButtonColumn();20break;21case GridColumnType.enProgressBar:22 column = new DataGridViewProgressBarColumn();23break;24case GridColumnType.enDateTimePicker:25 column = new CalendarColumn();26break;27case GridColumnType.enWorkStatus:28 column = new WorkStatusColumn();29break;30case GridColumnType.enImage:31 column = new DataGridViewImageColumn();32break;33case GridColumnType.enBrighter:34 column = new BrighterColumn();35break;36case GridColumnType.enVolume:37 column = new VolumeColumn();38break;39case GridColumnType.enTrackBar:40 column = new TrackBarColumn();41break;42case GridColumnType.enTimeZone:43 column = new TimeZoneColumn();44break;45case GridColumnType.enVolumeTrackBar:46 column = new VolumeTrackBarColumn();47break;48case GridColumnType.enBrighterTrackBar:49 column = new BrighterTrackBarColumn();50break;51 }52switch (sCaption.Substring(0, 1))53 {54case"^":55 column.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;56break;57case"<":58 column.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;59break;60case">":61 column.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;62break;63 }64 column.HeaderText = sCaption.Remove(0, 1);65 column.SortMode = SortMode;66 Grid.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;6768 = sName;69 column.Visible = bVisible;70 column.ReadOnly = bEdit;71//column.Width = iWidth * int.Parse(font.Size.ToString());72 column.Width = iWidth;73 Grid.Columns.Add(column);74 }75public enum GridColumnType76 {77 enTextBox = 0, //⽂本框78 enComboBox = 1,//下拉框79 enCheckBox = 2,//复选框80 enButton = 3, //按钮81 enProgressBar = 4,//进度条82 enDateTimePicker = 5, //时间框83 enWorkStatus = 6, //⼯作状态图标⽂字84 enImage = 7, //图⽚框85 enBrighter = 8,//亮度图标⽂字86 enVolume = 9, //⾳量图标⽂字87 enVolumeTrackBar = 10, //调节⾳量带滑块88 enTimeZone = 11, //⾃定义控件89 enTrackBar = 12, //滑块90 enBrighterTrackBar = 13, //滑块91 }92#region可以启⽤(禁⽤)按钮的⾃定义Grid按钮列和单元格93public class DataGridViewDisableButtonColumn : DataGridViewButtonColumn94 {95public DataGridViewDisableButtonColumn()96 {97this.CellTemplate = new DataGridViewDisableButtonCell();98 }99 }100public class DataGridViewDisableButtonCell : DataGridViewButtonCell101 {102private bool enabledValue;103public bool Enabled104 {105get106 {107return enabledValue;108 }109set110 {111 enabledValue = value;112 }113 }114115// 重写120return cell;121 }122123// 默认情况下,按钮可⽤124public DataGridViewDisableButtonCell()125 {126this.enabledValue = true;127 }128129protected override void Paint(Graphics graphics,130 Rectangle clipBounds, Rectangle cellBounds, int rowIndex,131 DataGridViewElementStates elementState, object value,132object formattedValue, string errorText,133 DataGridViewCellStyle cellStyle,134 DataGridViewAdvancedBorderStyle advancedBorderStyle,135 DataGridViewPaintParts paintParts)136 {137// 判断是否被禁⽤,如果被禁⽤,这绘制不可⽤的按钮样式138if (!this.enabledValue)139 {140//绘制背景141if ((paintParts & DataGridViewPaintParts.Background) ==142 DataGridViewPaintParts.Background)143 {144 SolidBrush cellBackground =145new SolidBrush(cellStyle.BackColor);146 graphics.FillRectangle(cellBackground, cellBounds);147 cellBackground.Dispose();148 }149150// 绘制边框151if ((paintParts & DataGridViewPaintParts.Border) ==152 DataGridViewPaintParts.Border)153 {154 PaintBorder(graphics, clipBounds, cellBounds, cellStyle,155 advancedBorderStyle);156 }157158// 计算按钮的绘制区域,⼤⼩等159 Rectangle buttonArea = cellBounds;160 Rectangle buttonAdjustment =161this.BorderWidths(advancedBorderStyle);162 buttonArea.X += buttonAdjustment.X;163 buttonArea.Y += buttonAdjustment.Y;164 buttonArea.Height -= buttonAdjustment.Height;165 buttonArea.Width -= buttonAdjustment.Width;166167// 绘制不可⽤情况下的按钮168 ButtonRenderer.DrawButton(graphics, buttonArea,169 PushButtonState.Disabled);170171// 绘制按钮⽂本172if (this.FormattedValue is String)173 {174 TextRenderer.DrawText(graphics,175 (string)this.FormattedValue,176this.DataGridView.Font,177 buttonArea, SystemColors.GrayText);178 }179 }180else181 {182// 绘制可⽤情况下的按钮183base.Paint(graphics, clipBounds, cellBounds, rowIndex,184 elementState, value, formattedValue, errorText,185 cellStyle, advancedBorderStyle, paintParts);186 }187 }188 }189#endregion;190191#region Grid添加进度条192public class DataGridViewProgressBarColumn : DataGridViewTextBoxColumn 193 {194public DataGridViewProgressBarColumn()195 {196this.CellTemplate = new DataGridViewProgressBarCell();197 }198public override DataGridViewCell CellTemplate199 {200get201 {202return base.CellTemplate;203 }204set205 {206if (!(value is DataGridViewProgressBarCell))207 {208throw new InvalidCastException("DataGridViewProgressBarCell");209 }210base.CellTemplate = value;211 }212 }213///<summary>214/// ProgressBar最⼤値215///</summary>216public int Maximum217 {218get219 {220return ((DataGridViewProgressBarCell)this.CellTemplate).Maximum;221 }222set223 {224if (this.Maximum == value)225return;226 ((DataGridViewProgressBarCell)this.CellTemplate).Maximum = value; 227228if (this.DataGridView == null)229return;230int rowCount = this.DataGridView.RowCount;231for (int i = 0; i < rowCount; i++)232 {233 DataGridViewRow r = this.DataGridView.Rows.SharedRow(i);234 ((DataGridViewProgressBarCell)r.Cells[this.Index]).Maximum = value; 235 }236 }237 }238///<summary>243get244 {245return ((DataGridViewProgressBarCell)this.CellTemplate).Mimimum;246 }247set248 {249if (this.Mimimum == value)250return;251 ((DataGridViewProgressBarCell)this.CellTemplate).Mimimum = value;252if (this.DataGridView == null)253return;254int rowCount = this.DataGridView.RowCount;255for (int i = 0; i < rowCount; i++)256 {257 DataGridViewRow r = this.DataGridView.Rows.SharedRow(i);258 ((DataGridViewProgressBarCell)r.Cells[this.Index]).Mimimum = value;259 }260 }261 }262public int BarValue263 {264get265 {266return ((DataGridViewProgressBarCell)this.CellTemplate).BarValue;267 }268set269 {270if (this.BarValue == value)271return;272 ((DataGridViewProgressBarCell)this.CellTemplate).BarValue = value;273if (this.DataGridView == null)274return;275int rowCount = this.DataGridView.RowCount;276for (int i = 0; i < rowCount; i++)277 {278 DataGridViewRow r = this.DataGridView.Rows.SharedRow(i);279 ((DataGridViewProgressBarCell)r.Cells[this.Index]).BarValue = value;280 }281 }282 }283 }284///<summary>285/// ProgressBarDataGridView286///</summary>287public class DataGridViewProgressBarCell : DataGridViewTextBoxCell288 {289public DataGridViewProgressBarCell()290 {291this.maximumValue = 100;292this.mimimumValue = 0;293 }294295296private int mValue = 0;297public int BarValue298 {299get300 {301return this.mValue;302 }303set304 {305this.mValue = value;306 }307 }308309private int maximumValue;310public int Maximum311 {312get313 {314return this.maximumValue;315 }316set317 {318this.maximumValue = value;319 }320 }321322private int mimimumValue;323public int Mimimum324 {325get326 {327return this.mimimumValue;328 }329set330 {331this.mimimumValue = value;332 }333 }334public override Type ValueType335 {336get337 {338return typeof(int);339 }340 }341public override object DefaultNewRowValue342 {343get344 {345return0;346 }347 }348public override object Clone()349 {350 DataGridViewProgressBarCell cell = (DataGridViewProgressBarCell)base.Clone(); 351 cell.Maximum = this.Maximum;352 cell.Mimimum = this.Mimimum;353return cell;354 }355356protected override void Paint(Graphics graphics,357 Rectangle clipBounds, Rectangle cellBounds,358int rowIndex, DataGridViewElementStates cellState,359object value, object formattedValue, string errorText,360 DataGridViewCellStyle cellStyle,361 DataGridViewAdvancedBorderStyle advancedBorderStyle,366 intValue = (int)value;367if (intValue < this.mimimumValue)368 intValue = this.mimimumValue;369if (intValue > this.maximumValue)370 intValue = this.maximumValue;371if (mValue != 0)372 {373 intValue = mValue;374 }375double rate = (double)(intValue - this.mimimumValue) / (this.maximumValue - this.mimimumValue);376if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)377 {378this.PaintBorder(graphics, clipBounds, cellBounds,379 cellStyle, advancedBorderStyle);380 }381 Rectangle borderRect = this.BorderWidths(advancedBorderStyle);382 Rectangle paintRect = new Rectangle(383 cellBounds.Left + borderRect.Left,384 cellBounds.Top + borderRect.Top,385 cellBounds.Width - borderRect.Right,386 cellBounds.Height - borderRect.Bottom);387bool isSelected = (cellState & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected;388 System.Drawing.Color bkColor;389if (isSelected && (paintParts & DataGridViewPaintParts.SelectionBackground) == DataGridViewPaintParts.SelectionBackground) 390 {391 bkColor = cellStyle.SelectionBackColor;392 }393else394 {395 bkColor = cellStyle.BackColor;396 }397if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)398 {399using (SolidBrush backBrush = new SolidBrush(bkColor))400 {401 graphics.FillRectangle(backBrush, paintRect);402 }403 }404 paintRect.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top);405 paintRect.Width -= cellStyle.Padding.Horizontal;406 paintRect.Height -= cellStyle.Padding.Vertical;407if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground)408 {409 Rectangle barBounds = new Rectangle(410 paintRect.Left, paintRect.Bottom - 3*cellBounds.Height/4,411 paintRect.Width, cellBounds.Height/2);412 barBounds.Width = (int)Math.Round(barBounds.Width * rate);413//ProgressBarRenderer.DrawHorizontalChunks(graphics, barBounds);414415 graphics.FillRectangle(System.Drawing.Brushes.Green, barBounds);416 barBounds = new Rectangle(417 paintRect.Left, paintRect.Bottom - cellBounds.Height,418 cellBounds.Width, cellBounds.Height);419 graphics.DrawRectangle(Pens.Wheat, barBounds);420//if (ProgressBarRenderer.IsSupported)421//{422////是否完全采⽤进度条的样式,此处主要修改了进度条的边框样式423////ProgressBarRenderer.DrawHorizontalBar(graphics, paintRect);424// Rectangle barBounds = new Rectangle(425// paintRect.Left, paintRect.Bottom - 9,426// paintRect.Width, 9);427// barBounds.Width = (int)Math.Round(barBounds.Width * rate);428////ProgressBarRenderer.DrawHorizontalChunks(graphics, barBounds);429430// graphics.FillRectangle(System.Drawing.Brushes.Green, barBounds);431// barBounds = new Rectangle(432// paintRect.Left, paintRect.Bottom - 9,433// cellBounds.Width, 9);434// graphics.DrawRectangle(Pens.Wheat, barBounds);435//}436//else437//{438////采⽤控件的背景⾊439// System.Drawing.SolidBrush brush = new System.Drawing.SolidBrush(bkColor);440// graphics.FillRectangle(brush, paintRect);441////graphics.FillRectangle(Brushes.White, paintRect);442////graphics.DrawRectangle(Pens.Black, paintRect);443// Rectangle barBounds = new Rectangle(444// paintRect.Left + 1, paintRect.Top + 1,445// paintRect.Width - 1, paintRect.Height - 1);446// barBounds.Width = (int)Math.Round(barBounds.Width * rate);447// graphics.FillRectangle(System.Drawing.Brushes.BurlyWood, barBounds);448//}449 }450if (this.DataGridView.CurrentCellAddress.X == this.ColumnIndex &&451this.DataGridView.CurrentCellAddress.Y == this.RowIndex &&452 (paintParts & DataGridViewPaintParts.Focus) ==453 DataGridViewPaintParts.Focus &&454this.DataGridView.Focused)455 {456457 Rectangle focusRect = paintRect;458 focusRect.Inflate(-3, -3);459 ControlPaint.DrawFocusRectangle(graphics, focusRect);460 }461if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground)462 {463if (value != null)464 {465//if (Math.Round(rate * 100) > 0 && Math.Round(rate * 100) <= 100)466//{467//string txt = string.Format("{0}%", Math.Round(rate * 100));468string txt = value.ToString();469 TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter;470 System.Drawing.Color fColor = cellStyle.ForeColor;471 paintRect.Inflate(-2, -2);472 TextRenderer.DrawText(graphics, txt, cellStyle.Font,473 paintRect, fColor, flags);474//}475 }476 }477if ((paintParts & DataGridViewPaintParts.ErrorIcon) ==478 DataGridViewPaintParts.ErrorIcon &&479this.DataGridView.ShowCellErrors &&480 !string.IsNullOrEmpty(errorText))481 {482 Rectangle iconBounds = this.GetErrorIconBounds(483 graphics, cellStyle, rowIndex);484 iconBounds.Offset(cellBounds.X, cellBounds.Y);490491#region Grid添加⼯作状态控件492public class WorkStatusColumn : DataGridViewTextBoxColumn493 {494public WorkStatusColumn()495 {496this.CellTemplate = new WorkStatusCell();497 }498public override DataGridViewCell CellTemplate499 {500get501 {502return base.CellTemplate;503 }504set505 {506if (!(value is WorkStatusCell))507 {508throw new InvalidCastException("DataGridViewVolumeCell"); 509 }510base.CellTemplate = value;511 }512 }513///<summary>514/// ProgressBar最⼤値515///</summary>516public int Maximum517 {518get519 {520return ((WorkStatusCell)this.CellTemplate).Maximum;521 }522set523 {524if (this.Maximum == value)525return;526 ((WorkStatusCell)this.CellTemplate).Maximum = value;527528if (this.DataGridView == null)529return;530int rowCount = this.DataGridView.RowCount;531for (int i = 0; i < rowCount; i++)532 {533 DataGridViewRow r = this.DataGridView.Rows.SharedRow(i); 534 ((WorkStatusCell)r.Cells[this.Index]).Maximum = value;535 }536 }537 }538///<summary>539/// ProgressBar最⼩値540///</summary>541public int Mimimum542 {543get544 {545return ((WorkStatusCell)this.CellTemplate).Mimimum;546 }547set548 {549if (this.Mimimum == value)550return;551 ((WorkStatusCell)this.CellTemplate).Mimimum = value;552if (this.DataGridView == null)553return;554int rowCount = this.DataGridView.RowCount;555for (int i = 0; i < rowCount; i++)556 {557 DataGridViewRow r = this.DataGridView.Rows.SharedRow(i); 558 ((WorkStatusCell)r.Cells[this.Index]).Mimimum = value;559 }560 }561 }562public int BarValue563 {564get565 {566return ((WorkStatusCell)this.CellTemplate).BarValue;567 }568set569 {570if (this.BarValue == value)571return;572 ((WorkStatusCell)this.CellTemplate).BarValue = value;573if (this.DataGridView == null)574return;575int rowCount = this.DataGridView.RowCount;576for (int i = 0; i < rowCount; i++)577 {578 DataGridViewRow r = this.DataGridView.Rows.SharedRow(i); 579 ((WorkStatusCell)r.Cells[this.Index]).BarValue = value;580 }581 }582 }583 }584///<summary>585///WorkStatusCell586///</summary>587public class WorkStatusCell : DataGridViewTextBoxCell588 {589public WorkStatusCell()590 {591this.maximumValue = 1;592this.mimimumValue = 0;593 }594595596private int mValue = 0;597public int BarValue598 {599get600 {601return this.mValue;602 }603set604 {605this.mValue = value;606 }607 }613 {614return this.maximumValue;615 }616set617 {618this.maximumValue = value;619 }620 }621622private int mimimumValue;623public int Mimimum624 {625get626 {627return this.mimimumValue;628 }629set630 {631this.mimimumValue = value;632 }633 }634public override Type ValueType635 {636get637 {638return typeof(int);639 }640 }641public override object DefaultNewRowValue642 {643get644 {645return0;646 }647 }648public override object Clone()649 {650 WorkStatusCell cell = (WorkStatusCell)base.Clone();651 cell.Maximum = this.Maximum;652 cell.Mimimum = this.Mimimum;653return cell;654 }655656protected override void Paint(Graphics graphics,657 Rectangle clipBounds, Rectangle cellBounds,658int rowIndex, DataGridViewElementStates cellState,659object value, object formattedValue, string errorText,660 DataGridViewCellStyle cellStyle,661 DataGridViewAdvancedBorderStyle advancedBorderStyle,662 DataGridViewPaintParts paintParts)663 {664int intValue = 0;665if (value is int)666 intValue = (int)value;667//if (intValue < this.mimimumValue)668// intValue = this.mimimumValue;669//if (intValue > this.maximumValue)670// intValue = this.maximumValue;671if (mValue != 0)672 {673 intValue = mValue;674 }675double rate = (double)(intValue - this.mimimumValue) / (this.maximumValue - this.mimimumValue);676if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)677 {678this.PaintBorder(graphics, clipBounds, cellBounds,679 cellStyle, advancedBorderStyle);680 }681 Rectangle borderRect = this.BorderWidths(advancedBorderStyle);682 Rectangle paintRect = new Rectangle(683 cellBounds.Left + borderRect.Left,684 cellBounds.Top + borderRect.Top,685 cellBounds.Width - borderRect.Right,686 cellBounds.Height - borderRect.Bottom);687bool isSelected = (cellState & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected;688 System.Drawing.Color bkColor;689if (isSelected && (paintParts & DataGridViewPaintParts.SelectionBackground) == DataGridViewPaintParts.SelectionBackground)690 {691 bkColor = cellStyle.SelectionBackColor;692 }693else694 {695 bkColor = cellStyle.BackColor;696 }697if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)698 {699using (SolidBrush backBrush = new SolidBrush(bkColor))700 {701 graphics.FillRectangle(backBrush, paintRect);702 }703 }704 paintRect.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top);705 paintRect.Width -= cellStyle.Padding.Horizontal;706 paintRect.Height -= cellStyle.Padding.Vertical;707if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground)708 {709//Rectangle barBounds = new Rectangle(710// paintRect.Left, paintRect.Bottom - cellBounds.Height,711// 40, cellBounds.Height);712if (intValue == 0)713 {714//graphics.DrawImage(Properties.Resources.btncheckoff, paintRect.X+cellBounds.Size.Width/4, paintRect.Bottom - 3*cellBounds.Height / 4 , 60,15);715 graphics.DrawImage(Properties.Resources.btncheckoff, paintRect.X + cellBounds.Size.Width / 4, paintRect.Bottom - cellBounds.Height, 60, cellBounds.Size.Height); 716717 }718else719 {720//graphics.DrawImage(Properties.Resources.btncheckon, paintRect.X + cellBounds.Size.Width / 4, paintRect.Bottom - 3 * cellBounds.Height / 4, 60, 15);721 graphics.DrawImage(Properties.Resources.btncheckon, paintRect.X + cellBounds.Size.Width / 4, paintRect.Bottom - cellBounds.Height, 60, cellBounds.Size.Height); 722 }723 }724if (this.DataGridView.CurrentCellAddress.X == this.ColumnIndex &&725this.DataGridView.CurrentCellAddress.Y == this.RowIndex &&726 (paintParts & DataGridViewPaintParts.Focus) ==727 DataGridViewPaintParts.Focus &&728this.DataGridView.Focused)729 {730735if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.ContentForeground)736 {737if (value != null)738 {739740string txt = value.ToString();741//TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter;742 System.Drawing.Color fColor = cellStyle.ForeColor;743 paintRect.Inflate(-2, -2);744//TextRenderer.DrawText(graphics, txt, cellStyle.Font, paintRect, fColor, flags);745 }746 }747if ((paintParts & DataGridViewPaintParts.ErrorIcon) ==748 DataGridViewPaintParts.ErrorIcon &&749this.DataGridView.ShowCellErrors &&750 !string.IsNullOrEmpty(errorText))751 {752 Rectangle iconBounds = this.GetErrorIconBounds(753 graphics, cellStyle, rowIndex);754 iconBounds.Offset(cellBounds.X, cellBounds.Y);755this.PaintErrorIcon(graphics, iconBounds, cellBounds, errorText);756 }757 }758 }759#endregion760761#region Grid添加⽇期控件762public class CalendarCell : DataGridViewTextBoxCell763 {764public CalendarCell()765 : base()766 {767//使⽤简短⽇期格式.768this.Style.Format = "HH:mm:ss";769 }770public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) 771 {772//将编辑控件的值设置成当前单元格的值.773base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);774//775 CalendarEditingControl ctl = DataGridView.EditingControl as CalendarEditingControl;776try777 {778 ctl.Value = (DateTime)this.Value;779 }780catch (Exception)781 {782/*当数据库中对应⽇期为空(⽐如在添加数据时),在这⾥设置⽇期的默认值。
为什么你的自定义View wrap_content不起作用?
为什么你的自定义View wrap_content不起作用?前言自定义View是Android开发中非常常用的知识可是,在使用过程中,有些开发者会发现:为什么自定义View 中设置的wrap_content属性不起作用(与match_parent相同作用)?今天,我将全面分析上述问题并给出解决方案。
1. 问题描述在使用自定义View时,View宽/ 高的wrap_content属性不起自身应有的作用,而且是起到与match_parent相同作用。
wrap_content与match_parent区别:1. wrap_content:视图的宽/高被设定成刚好适应视图内容的最小尺寸2. match_parent:视图的宽/高被设置为充满整个父布局(在Android API 8之前叫作fill_parent)其实这里有两个问题:问题1:wrap_content属性不起自身应有的作用问题2:wrap_content起到与match_parent相同的作用问题分析问题出现在View的宽/ 高设置,那我们直接来看自定义View绘制中第一步对View宽/ 高设置的过程:measure过程中的onMeasure()方法onMeasure()protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//参数说明:View的宽/ 高测量规格//setMeasuredDimension() 用于获得View宽/高的测量值//这两个参数是通过getDefaultSize()获得的setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}继续往下看getDefaultSize()getDefaultSize()作用:根据View宽/高的测量规格计算View的宽/高值源码分析如下:public static int getDefaultSize(int size, int measureSpec) {//参数说明:// 第一个参数size:提供的默认大小// 第二个参数:宽/高的测量规格(含模式& 测量大小)//设置默认大小int result = size;//获取宽/高测量规格的模式& 测量大小int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {// 模式为UNSPECIFIED时,使用提供的默认大小// 即第一个参数:sizecase MeasureSpec.UNSPECIFIED:result = size;break;// 模式为AT_MOST,EXACTL Y时,使用View测量后的宽/高值// 即measureSpec中的specSizecase MeasureSpec.AT_MOST:case MeasureSpec.EXACTL Y:result = specSize;break;}//返回View的宽/高值return result;}从上面发现:在getDefaultSize()的默认实现中,当View的测量模式是AT_MOST或EXACTL Y时,View 的大小都会被设置成子View MeasureSpec的specSize。
自定义view的基本流程
自定义view的基本流程1.自定义View的意义自定义View是开发Android应用的常见需求,它可以实现各种特殊的UI效果和交互效果。
有时候,自带的一些View无法满足我们的需求,而自定义一个View可以帮助我们实现想要的效果。
2.自定义View的基本流程要实现一个自定义View,通常需要经过以下基本流程:2.1创建自定义View类首先,我们需要创建一个自定义View的类,并继承自View或其子类,比如ImageView、TextView等。
在类中,我们可以重写onMeasure()、onLayout()和onDraw()等方法来实现自定义View的特殊行为。
2.2声明自定义View的属性如果我们希望在XML布局文件中使用自定义View,并可以在代码中设置属性值,就需要在类中声明自定义属性。
这可以通过在res/values/attrs.xml文件中定义属性集合来实现。
2.3实现自定义View的布局在onMeasure()方法中,我们可以指定自定义View在测量时占用的空间大小。
在onLayout()方法中,我们可以指定子View的位置。
2.4实现自定义View的绘制在onDraw()方法中,我们可以通过调用Canvas的API来实现自定义View的绘制效果。
例如,我们可以通过Path绘制自定义的图形,通过Paint设置绘制时的样式和颜色等。
2.5对自定义View进行测试最后,我们需要通过手动测试、单元测试、自动化UI测试等方式,对自定义View进行测试和验证。
3.自定义View的实例以下是一个简单的自定义View实例,用于显示一个可以按下和弹起的按钮:```public class MyButton extends View{private boolean isPressed=false;public MyButton(Context context){super(context);}public MyButton(Context context,AttributeSet attrs) {super(context,attrs);}@Overrideprotected void onDraw(Canvas canvas){super.onDraw(canvas);Paint paint=new Paint();paint.setColor(isPressed?Color.RED:Color.GREEN);canvas.drawRect(0,0,getWidth(),getHeight(), paint);}@Overridepublic boolean onTouchEvent(MotionEvent event){if(event.getAction()==MotionEvent.ACTION_DOWN) {isPressed=true;invalidate();return true;}else if(event.getAction()==MotionEvent.ACTION_UP){isPressed=false;invalidate();return true;}return super.onTouchEvent(event);}}```以上代码中,我们实现了一个MyButton类,当MyButton被按下时,它的背景颜色会变成红色,当MyButton被弹起时,它的背景颜色会回到绿色。
Android中自定义样式与View的构造函数中的第三个参数defStyle的意义
零、序一、自定义Style二、在XML中为属性声明属性值1. 在layout中定义属性2. 设置Style3. 通过Theme指定三、在运行时获取属性值1. View的第三个构造函数的第三个参数defStyle2. obtailStyledAttributes3. Example四、结论与代码下载零、序系统自带的View可以在xml中配置属性,对于写的好的Custom View同样可以在xml中配置属性,为了使自定义的View的属性可以在xml中配置,需要以下4个步骤:1.1.通过<declare-styleable>为自定义View添加属性2.在xml中为相应的属性声明属性值3.在运行时(一般为构造函数)获取属性值4.将获取到的属性值应用到View怎么将获取到的属性值应用到View就不用说了,自己定义的属性什么用处自己肯定是清楚的,所以接下来看一下前三点。
通过<declare-styleable>元素声明Custom View需要的属性即可,下面是一个例子,文件是res/values/attrs.xml<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="Customize"><attr name="attr_one" format="string"/><attr name="attr_two" format="string"/><attr name="attr_three" format="string"/><attr name="attr_four" format="string"/></declare-styleable><attr name="CustomizeStyle" format="reference"/></resources>在上述xml中,我们声明了Customize与CustomizeSyle,Customize包含了attr_one、attr_two、attr_three与attr_four四个attribute,CustomizeStyle也是一个attribute,但是却没有声明在declare-styleable中。
WPF自定义TreeView控件样式,仿QQ联系人列表
WPF⾃定义TreeView控件样式,仿QQ联系⼈列表⼀、前⾔TreeView控件在项⽬中使⽤⽐较频繁,普通的TreeView并不能满⾜我们的需求。
因此我们需要滴对TreeView进⾏改造。
下⾯的内容将介绍仿QQ联系⼈TreeView样式及TreeView数据绑定⽅法。
⼆、TreeView仿QQ联系⼈列表准确的说不是仿QQ联系⼈列表,这个TreeView样式作为组织架构来使⽤更好。
废话不多说,先看效果:2.1、基本思路像这种联系⼈列表⼀般涉及到多层级数据,⽽且有很多数据是需要动态更新的,如果通过⼿动⼀条条增加数据反⽽更复杂,⽽且不⽅便。
因此为了绑定数据⽅便我们使⽤分层模板HierarchicalDataTemplate。
分层模板中存在两种样式,⼀种是分组样式,⼀种是⼈员样式。
不管是分组还是⼈员绑定的都是对象,这样我们在对象中添加⼀个属性来辨别是否为分组-IsGrouping。
默认的TreeView控件四周会有边距,因此需要设置下TreeView的样式。
另外⿏标经过和⿏标选中的背景⾊需要变化,因此还需要设置TreeViewItem的样式。
根据思路,我们需要设置三个样式,TreeView样式,TreeViewItem样式,HierarchicalDataTemplate分层模板样式。
另外为了⾃动计算下⼀级的边距,我们需要添加⼀个转换器IndentConverter。
还需要⼀个转换器需要将布尔类型的IsGrouping转换为Visibility,还需要⼀个转换器来对Visibility取反。
这样三个样式,三个转换器。
就可以实现我们上⾯的效果,另外还可以进⾏动态数据绑定。
2.2、样式代码HierarchicalDataTemplate分层模板样式代码<HierarchicalDataTemplate x:Key="ItemNode" ItemsSource="{Binding Children,Mode=TwoWay}"><Grid Background="Transparent"><Grid.Resources><convert:BoolToVisible x:Key="boolToVisible"/><convert:VisibleToReverse x:Key="visibleToReverse"/></Grid.Resources><Grid MinHeight="30" x:Name="userinfo" Background="Transparent" Margin="-5 0 0 0" Visibility="{Binding Visibility,ElementName=groupinginfo,Converter={StaticResource visibleToReverse}}"><Grid Height="50" x:Name="grid"><Border Background="#62acf9" Width="40" Height="40" CornerRadius="4" HorizontalAlignment="Left" Margin="0 0 0 0"><TextBlock Text="{Binding SurName}" FontSize="23" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center"/></Border><TextBlock Text="{Binding Name}" Margin="50 7 0 0" FontSize="13"/><TextBlock Text="{Binding Info}" Foreground="#808080" Margin="50 30 0 0"/><TextBlock Text="{Binding Count,StringFormat={}{0}⼈}" Foreground="#808080" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0 0 5 0"/></Grid></Grid><StackPanel MinHeight="25" x:Name="groupinginfo" Orientation="Horizontal" Background="Transparent" HorizontalAlignment="Left" Visibility="{Binding IsGrouping,Converter={StaticResource boolToVisible}} <TextBlock Text="{Binding DisplayName}" Margin="3 0" VerticalAlignment="Center" HorizontalAlignment="Left"/></StackPanel></Grid></HierarchicalDataTemplate>TreeViewItem样式代码<Style x:Key="DefaultTreeViewItem" TargetType="{x:Type TreeViewItem}"><Setter Property="MinHeight" Value="25"/><Setter Property="Background" Value="Transparent"/><Setter Property="SnapsToDevicePixels" Value="True"/><Setter Property="Margin" Value="0"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type TreeViewItem}"><ControlTemplate.Resources><convert:IndentConverter x:Key="indentConverter"/></ControlTemplate.Resources><Grid Background="Transparent"><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Border Name="itemBackground" Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"Padding="{TemplateBinding Padding}"><Grid Background="Transparent"><Grid x:Name="ItemRoot" Margin="{Binding Converter={StaticResource indentConverter},RelativeSource={RelativeSource TemplatedParent}}" Background="Transparent"><Grid.ColumnDefinitions><ColumnDefinition Width="16"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><ToggleButton x:Name="Expander" HorizontalAlignment="Left" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"><ToggleButton.Style><Style TargetType="{x:Type ToggleButton}"><Setter Property="Focusable" Value="False"/><Setter Property="Width" Value="16"/><Setter Property="Height" Value="16"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type ToggleButton}"><Border Background="Transparent" Height="16" Padding="5" Width="16"><Path x:Name="ExpandPath" Data="M0,0 L0,6 L6,0 z" Fill="#66645e" Stroke="#66645e"><Path.RenderTransform><RotateTransform Angle="135" CenterY="3" CenterX="3"/></Path.RenderTransform></Path></Border><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter Property="RenderTransform" TargetName="ExpandPath"><Setter.Value><RotateTransform Angle="180" CenterY="3" CenterX="3"/></Setter.Value></Setter><Setter Property="Fill" TargetName="ExpandPath" Value="#66645e"/><Setter Property="Stroke" TargetName="ExpandPath" Value="#66645e"/></Trigger><Trigger Property="IsMouseOver" Value="True"><Setter Property="Stroke" TargetName="ExpandPath" Value="#66645e"/><Setter Property="Fill" TargetName="ExpandPath" Value="#66645e"/></Trigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsMouseOver" Value="True"/><Condition Property="IsChecked" Value="True"/></MultiTrigger.Conditions><Setter Property="Stroke" TargetName="ExpandPath" Value="#66645e"/><Setter Property="Fill" TargetName="ExpandPath" Value="#66645e"/></MultiTrigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style></ToggleButton.Style></ToggleButton><ContentPresenter Grid.Column="1" x:Name="PART_Header" ContentSource="Header"HorizontalAlignment="Stretch"></ContentPresenter></Grid></Grid></Border><ItemsPresenter x:Name="ItemsHost" Grid.Row="1"/></Grid><ControlTemplate.Triggers><DataTrigger Binding="{Binding IsGrouping}" Value="false"><Setter Property="Visibility" TargetName="Expander" Value="Hidden"/></DataTrigger><Trigger Property="HasItems" Value="False"><Setter Property="Visibility" TargetName="Expander" Value="Collapsed"/></Trigger><Trigger Property="IsExpanded" Value="False"><Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/></Trigger><Trigger Property="IsSelected" Value="True"><Setter Property="Background" TargetName="itemBackground" Value="#FAE388"/></Trigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsFocused" Value="False"/><Condition SourceName="itemBackground" Property="IsMouseOver" Value="true"/></MultiTrigger.Conditions><Setter Property="Background" Value=" #fceeb9" TargetName="itemBackground"/></MultiTrigger><Trigger Property="IsEnabled" Value="False"><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter></Style>TreeView样式代码<Style x:Key="DefaultTreeView" TargetType="{x:Type TreeView}"><Setter Property="ScrollViewer.CanContentScroll" Value="True"/><Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter><Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/><Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/><Setter Property="ItemContainerStyle" Value="{StaticResource DefaultTreeViewItem}"></Setter><Setter Property="Padding" Value="0"/><Setter Property="ItemsPanel"><Setter.Value><ItemsPanelTemplate><VirtualizingStackPanel IsItemsHost="True" Margin="0"/></ItemsPanelTemplate></Setter.Value></Setter></Style>2.3、转换器代码public class IndentConverter : IValueConverter{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){double colunwidth = 10;double left = 0.0;UIElement element = value as TreeViewItem;while (element.GetType() != typeof(TreeView)){element = (UIElement)VisualTreeHelper.GetParent(element);if (element.GetType() == typeof(TreeViewItem))left += colunwidth;}return new Thickness(left, 0, 0, 0);}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){throw new NotImplementedException();}}public class BoolToVisible : IValueConverter{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){if ((bool)value)return Visibility.Visible;elsereturn Visibility.Collapsed;}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){throw new NotImplementedException();}}public class VisibleToReverse : IValueConverter{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){if ((Visibility)value == Visibility.Visible)return Visibility.Collapsed;elsereturn Visibility.Visible;}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){throw new NotImplementedException();}}2.4、引⽤⽰例<TreeView x:Name="TreeViewOrg" BorderThickness="1" BorderBrush="#BBB" Background="Transparent" Width="280" Height="500" Margin="10" ItemTemplate="{StaticResource ItemNode}" Style="{StaticResource </TreeView>2.5、初始化数据源及绑定对象public MainWindow(){InitializeComponent();OrgList = new ObservableCollection<OrgModel>(){{IsGrouping=true,DisplayName="单位名称(3/7)",Children=new ObservableCollection<OrgModel>(){new OrgModel(){IsGrouping=true,DisplayName="未分组联系⼈(2/4)",Children=new ObservableCollection<OrgModel>() {new OrgModel(){IsGrouping=false,SurName="刘",Name="刘棒",Info="我要⾛向天空!",Count=3}}}},}};TreeViewOrg.ItemsSource = OrgList;}public ObservableCollection<OrgModel> OrgList { get; set; }public class OrgModel{public bool IsGrouping { get; set; }public ObservableCollection<OrgModel> Children { get; set; }public string DisplayName { get; set; }public string SurName { get; set; }public string Name { get; set; }public string Info { get; set; }public int Count { get; set; }}所有代码已经上传到github:。
Android自定义view之仿支付宝芝麻信用仪表盘
Android自定义view之仿支付宝芝麻信用仪表盘Android自定义view之仿支付宝芝麻信用仪表盘自定义view练习仿支付宝芝麻信用的仪表盘对比图:首先是自定义一些属性,可自己再添加,挺基础的,上代码[html] view plain copy<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="RoundIndicatorView"><!--最大数值--><attr name="maxNum" format="integer"/><!--圆盘起始角度--><attr name="startAngle" format="integer"/><!--圆盘扫过的角度--><attr name="sweepAngle" format="integer"/></declare-styleable></resources>接着在构造方法里初始化自定义属性和画笔:[java] view plain copyprivate void initAttr(AttributeSet attrs) {TypedArray array =context.obtainStyledAttributes(attrs,R.styleable.RoundIndicatorView);maxNum = array.getInt(R.styleable.RoundIndicatorView_maxNum,500);startAngle =array.getInt(R.styleable.RoundIndicatorView_startAngle,160);sweepAngle =array.getInt(R.styleable.RoundIndicatorView_sweepAngle,220);//内外圆弧的宽度sweepInWidth = dp2px(8);sweepOutWidth = dp2px(3);array.recycle();}private void initPaint() {paint = new Paint(Paint.ANTI_ALIAS_FLAG);paint.setDither(true);paint.setStyle(Paint.Style.STROKE);paint.setColor(0xffffffff);paint_2 = new Paint(Paint.ANTI_ALIAS_FLAG);paint_3 = new Paint(Paint.ANTI_ALIAS_FLAG);paint_4 = new Paint(Paint.ANTI_ALIAS_FLAG); }接下来重写onMeasure,也是比较简单,对于不是确定值的直接给定300*400的大小:[java] view plain copy@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int wSize = MeasureSpec.getSize(widthMeasureSpec);int wMode = MeasureSpec.getMode(widthMeasureSpec);int hSize = MeasureSpec.getSize(heightMeasureSpec);int hMode = MeasureSpec.getMode(heightMeasureSpec);if (wMode == MeasureSpec.EXACTLY ){mWidth = wSize;}else {mWidth =dp2px(300);}if (hMode == MeasureSpec.EXACTLY ){mHeight= hSize;}else {mHeight =dp2px(400);}setMeasuredDimension(mWidth,mHeight);}核心部分onDraw来了,注意圆的半径不要在构造方法里就去设置,那时候是得不到view的宽高值的,所以我在onDraw方法里设置半径,默认就view宽度的1/4吧。
自定义View实现三角形(正三角,倒三角)
⾃定义View实现三⾓形(正三⾓,倒三⾓)⾃定义的属性如下:<declare-styleable name="TriangleView"><!--模式--><attr name="tlv_mode"><!--倒三⾓--><enum name="inverted" value="0"/><!--正三⾓--><enum name="regular" value="1"/></attr><!--颜⾊--><attr name="tlv_color" format="color|reference"/></declare-styleable>具体代码如下:import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.support.annotation.IntDef;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;import ng.annotation.Retention;import ng.annotation.RetentionPolicy;/*** 三⾓形*/public class TriangleView extends View{private Paint paint;private Path path;private int color;private int mode;private final int DEFAULT_WIDTH=48;private final int DEFAULT_HEIGHT=24;private int width = 0;private int height =0;/*** 倒三⾓*/public static final int INVERTED = 0;/*** 正三⾓*/public static final int REGULAR = 1;@IntDef({INVERTED, REGULAR})@Retention(RetentionPolicy.SOURCE)public @interface ShapeMode {}public TriangleView(Context context) {this(context,null);}public TriangleView(Context context, @Nullable AttributeSet attrs) {this(context,attrs,0);}public TriangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context,attrs);}private void init(Context context,AttributeSet attrs){TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TriangleView);color = typedArray.getColor(R.styleable.TriangleView_tlv_color, Color.BLACK);mode = typedArray.getInt(R.styleable.TriangleView_tlv_mode, INVERTED);typedArray.recycle();paint = new Paint();paint.setColor(color);paint.setAntiAlias(true);paint.setStyle(Paint.Style.FILL);path= new Path();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);width = measureSize(widthMeasureSpec, DEFAULT_WIDTH);height = measureSize(heightMeasureSpec, DEFAULT_HEIGHT);setMeasuredDimension(width, height);}private int measureSize(int measureSpec, int defaultSize) {int newSize = 0;int mode = MeasureSpec.getMode(measureSpec);int size = MeasureSpec.getSize(measureSpec);switch (mode) {case MeasureSpec.AT_MOST:newSize = Math.min(size, defaultSize);break;case MeasureSpec.EXACTLY:newSize = size;break;case MeasureSpec.UNSPECIFIED:newSize = defaultSize;break;}return newSize;}public void setColor(int color){this.color=color;paint.setColor(color);invalidate();}public void setMode(@ShapeMode int mode){this.mode=mode;invalidate();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawTriangle(canvas);}private void drawTriangle(Canvas canvas) {if(mode==INVERTED) {path.moveTo(0f, 0f);path.lineTo(width, 0f);path.lineTo(width / 2.0f, height);}else {path.moveTo(width/2.0f,0f);path.lineTo(0,height);path.lineTo(width,height);}path.close();canvas.drawPath(path, paint);}}。
自定义view的绘制流程
自定义view的绘制流程自定义View的绘制流程。
在Android开发中,自定义View是非常常见的需求,通过自定义View可以实现各种炫酷的效果,满足各种个性化的设计需求。
但是,自定义View的绘制流程并不是那么简单,需要我们对View的绘制机制有一定的了解,才能够高效地实现自定义View的绘制。
本文将从自定义View的基本概念开始,逐步介绍自定义View的绘制流程。
1. 自定义View的基本概念。
自定义View是指在Android开发中,通过继承View或者ViewGroup来创建自定义的UI控件。
通过自定义View,我们可以实现各种各样的UI效果,比如自定义的按钮样式、进度条样式、图表样式等。
自定义View的核心就是重写View或者ViewGroup的绘制方法,以实现我们所需的UI效果。
2. 自定义View的绘制流程。
自定义View的绘制流程可以分为以下几个步骤:(1)onMeasure,测量View的大小。
在onMeasure方法中,我们需要通过调用setMeasuredDimension方法来设置View的大小。
在这个方法中,我们需要考虑View的宽度和高度,以及View的测量模式。
通常情况下,我们需要根据View的内容和父容器的大小来计算View的大小,并根据测量模式来确定View的最终大小。
(2)onLayout,确定View的位置。
在onLayout方法中,我们需要通过调用layout方法来确定View在父容器中的位置。
在这个方法中,我们需要考虑View的左上右下四个位置,以及父容器的大小和布局方式,来确定View的最终位置。
(3)onDraw,绘制View的内容。
在onDraw方法中,我们需要通过Canvas来绘制View的内容。
在这个方法中,我们可以使用各种绘制方法来绘制文字、图形、图片等内容,以实现我们所需的UI效果。
在绘制过程中,我们还可以通过Paint来设置绘制的样式、颜色、字体等属性,以实现更加丰富的UI效果。
Android自定义ViewGroup(二)——带悬停标题的ExpandableListView
Android自定义ViewGroup(二)——带悬停标题的ExpandableListView也就是说,在某一个分组内部滚动时,要求分组标题悬停,当滚出该分组范围时,把标题顶出去,悬停下一个分组的标题。
正好看到一个比较有趣的思路,做了一个实现,在这里分享一下。
代码结构如下,基本上是一个MVC的架构:既然是点击可收缩展开的列表,显然要用ExpandableListView,关于这个类的用法这里就不赘述了,网上一搜一大把,其实跟ListView的用法差不多,不过它帮你分了组,所以原来Adapter里的getView()就变成了getGroupView()和getChildView(),getCount()就变成了getGroupCount()等等。
另外既然要支持收缩展开,必然会提供collapseGroup()和expandGroup()等接口。
下面分析如何添加悬停标题,其实精华部分就一句话:悬停标题是画上去的,而不是加到view hierarchy里去,具体根据滚动的情况确定如何画。
首先我们来写一个DockingExapandableListView类,继承自ExpandableListView,包含一个View类型的成员变量mDockingHeader。
一、重写onMeasure()和onLayout()方法[java] view plain copy 在CODE上查看代码片派生到我的代码片@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (mDockingHeader != null) {measureChild(mDockingHeader, widthMeasureSpec, heightMeasureSpec);mDockingHeaderWidth = mDockingHeader.getMeasuredWidth();mDockingHeaderHeight = mDockingHeader.getMeasuredHeight();}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);if (mDockingHeader != null) {yout(0, 0, mDockingHeaderWidth, mDockingHeaderHeight);}}这个比较简单,就是测量一下这个标题视图的宽度和高度。
Unity UGUI自定义树形菜单(TreeView)
Unity UGUI自定义树形菜单(TreeView)如果你需要的也是这种效果,那你就来对地方了!目前,我们这个树形菜单展现出来的功能如下:1、可以动态配置数据源;2、点击每个元素的上下文菜单按钮(也就是图中的三角形按钮),可以收缩或展开它的子元素;3、可以单独判断某一元素的复选框是否被勾选,或者直接获取当前树形菜单中所有被勾选的元素;4、树形菜单统一控制其下所有子元素按钮的事件分发;5、可自动调节的滚动视野边缘,根据当前可见的子元素数量进行横向以及纵向的伸缩;一、首先,我们先制作子元素的模板(Template),也就是图中菜单的单个元素,用它来根据数据源动态克隆出多个子元素,这里的话,很显然我们的模板是由两个Button加一个Toggle和一个Text组成的,如下:二、我们的每个子元素都会携带一个TreeViewItem脚本,用于描述自身在整个树形菜单中与其他元素的父子关系,而整个树形菜单的控制由TreeViewControl来实现,首先,TreeViewControl会根据提供的数据源来生成所有的子元素,当然,改变数据源之后进行重新生成的时候也是这个方法,干的事情很简单,就是用模板不停的创建元素,并给他们建立父子关系:[csharp] view plain copy/// <summary>/// 生成树形菜单/// </summary>public void GenerateTreeView(){//删除可能已经存在的树形菜单元素if (_treeViewItems != null){for (int i = 0; i < _treeViewItems.Count; i++){Destroy(_treeViewItems[i]);}_treeViewItems.Clear();}//重新创建树形菜单元素_treeViewItems = new List<GameObject>();for (int i = 0; i < Data.Count; i++){GameObject item = Instantiate(Template);if (Data[i].ParentID == -1){item.GetComponent<TreeViewItem>().SetHierarchy(0);item.GetComponent<TreeViewItem>().SetParent(null);}else{TreeViewItem tvi = _treeViewItems[Data[i].ParentID].GetComponent<TreeViewItem>();item.GetComponent<TreeViewItem>().SetHierarchy(tvi.GetHierarchy() + 1);item.GetComponent<TreeViewItem>().SetParent(tvi);tvi.AddChildren(item.GetComponent<TreeViewItem>());} = "TreeViewItem";item.transform.FindChild("TreeViewText").GetComponent<Text>().text = Data[i].Name;item.transform.SetParent(TreeItems);item.transform.localPosition = Vector3.zero;item.transform.localScale = Vector3.one;item.transform.localRotation = Quaternion.Euler(Vector3.zero);item.SetActive(true);_treeViewItems.Add(item);}}三、树形菜单生成完毕之后此时所有元素虽然都记录了自身与其他元素的父子关系,但他们的位置都是在Vector3.zero的,毕竟我们的菜单元素在创建的时候都是一股脑儿的丢到原点位置的,创建君可不管这么多元素挤在一堆会不会憋死,好吧,之后规整列队的事情就交给刷新君来完成了,刷新君玩的一手好递归,它会遍历所有元素并剔除不可见的元素(也就是点击三角按钮隐藏了),并将它们一个一个的重新排列整齐,子排在父之后,孙排在子之后,以此类推......它会遍历每个元素的子元素列表,发现子元素可见便进入子元素列表,发现孙元素可见便进入孙元素列表:[csharp] view plain copy/// <summary>/// 刷新树形菜单/// </summary>public void RefreshTreeView(){_yIndex = 0;_hierarchy = 0;//复制一份菜单_treeViewItemsClone = new List<GameObject>(_treeViewItems);//用复制的菜单进行刷新计算for (int i = 0; i < _treeViewItemsClone.Count; i++){//已经计算过或者不需要计算位置的元素if (_treeViewItemsClone[i] == null || !_treeViewItemsClone[i].activeSelf){continue;}TreeViewItem tvi = _treeViewItemsClone[i].GetComponent<TreeViewItem>();_treeViewItemsClone[i].GetComponent<RectTransform>().localPosition = new Vector3(tvi.GetHierarchy() * HorizontalItemSpace, _yIndex,0);_yIndex += (-(ItemHeight + VerticalItemSpace));if (tvi.GetHierarchy() > _hierarchy){_hierarchy = tvi.GetHierarchy();}//如果子元素是展开的,继续向下刷新if (tvi.IsExpanding){RefreshTreeViewChild(tvi);}_treeViewItemsClone[i] = null;}//重新计算滚动视野的区域float x = _hierarchy * HorizontalItemSpace + ItemWidth;float y = Mathf.Abs(_yIndex);transform.GetComponent<ScrollRect>().content.sizeDelta = new Vector2(x, y);//清空复制的菜单_treeViewItemsClone.Clear();}/// <summary>/// 刷新元素的所有子元素/// </summary>void RefreshTreeViewChild(TreeViewItem tvi){for (int i = 0; i < tvi.GetChildrenNumber(); i++){tvi.GetChildrenByIndex(i).gameObject.GetComponent<RectTransform>().localPosition = new Vector3(tvi.GetChildrenByIndex(i).GetHierarchy() * HorizontalItemSpace, _yIndex, 0);_yIndex += (-(ItemHeight + VerticalItemSpace));if (tvi.GetChildrenByIndex(i).GetHierarchy() > _hierarchy){_hierarchy = tvi.GetChildrenByIndex(i).GetHierarchy();}//如果子元素是展开的,继续向下刷新if (tvi.GetChildrenByIndex(i).IsExpanding){RefreshTreeViewChild(tvi.GetChildrenByIndex(i));}int index = _treeViewItemsClone.IndexOf(tvi.GetChildrenByIndex(i).gameObject);if (index >= 0){_treeViewItemsClone[index] = null;}}}我这里将所有的元素复制了一份用于计算位置,主要就是为了防止在进行一轮刷新时某个元素被访问两次或以上,因为刷新的时候会遍历所有可见元素,如果第一次访问了元素A(元素A的位置被刷新),根据元素A的子元素列表访问到了元素B(元素B的位置被刷新),一直到达子元素的底部后,当不存在更深层次的子元素时,那么返回到元素A之后的元素继续访问,这时在所有元素列表中元素B可能在元素A之后,也就是说元素B已经通过父元素访问过了,不需要做再次访问,他的位置已经是最新的了,而之后根据列表索引很可能再次访问到元素B,如果是这样的话元素B的位置又要被刷新一次,甚至多次,性能影响不说,第二次计算的位置已经不是正确的位置了。
Android自定义view实现日历打卡签到
Android⾃定义view实现⽇历打卡签到本⽂实例为⼤家分享了Android⾃定义view实现⽇历打卡签到的具体代码,供⼤家参考,具体内容如下1.说明⾃⼰写⼀个view实现每天签到的功能,设置背景图⽚2.效果图3.主界⾯package com.example.myapplication30;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import java.util.ArrayList;import java.util.Calendar;import java.util.List;public class MainActivity extends AppCompatActivity {//参考⽹址:https:///MacaoPark/article/details/102069775private TextView mTvDaySum;private TextView mTvMonth;private SignView mCvCalendar;private List<SignEntity> data;private Calendar calendar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);mTvDaySum = findViewById(R.id.punch_tv_day_sum);mTvMonth = findViewById(R.id.punch_tv_month);mCvCalendar = findViewById(R.id.punch_cv_calendar);}@Overrideprotected void onStart() {super.onStart();onReady();}@SuppressLint("SetTextI18n")private void onReady() {calendar = Calendar.getInstance();int year = calendar.get(Calendar.YEAR);int month = calendar.get(Calendar.MONTH);//int date = calendar.get(Calendar.DATE);int dayOfMonthToday = calendar.get(Calendar.DAY_OF_MONTH);List<SignDate> signDates = new ArrayList<>();signDates.add(new SignDate(2021, 5, 1, true));signDates.add(new SignDate(2021, 5, 2, true));signDates.add(new SignDate(2021, 5, 3, true));signDates.add(new SignDate(2021, 5, 4, true));signDates.add(new SignDate(2021, 5, 5, true));mTvDaySum.setText("本期连续登录\t"+signDates.size()+"\t天");mTvMonth.setText(year+"年"+getResources().getStringArray(R.array.month_array)[month]+"\t"+dayOfMonthToday+"⽇"); data = new ArrayList<>();for (int i = 1; i <= dayOfMonthToday; i++) {SignEntity signEntity = new SignEntity();if (i == dayOfMonthToday) {signEntity.setDayType(2);} else {signEntity.setDayType(1);}for (int j = 0; j < signDates.size(); j++) {if (signDates.get(j).getDay() == i) {signEntity.setDayType(0);break;} else if (dayOfMonthToday == i) {signEntity.setDayType(2);} else {signEntity.setDayType(1);}}data.add(signEntity);}SignAdapter signAdapter = new SignAdapter(data);mCvCalendar.setAdapter(signAdapter);}}4.适配器package com.example.myapplication30;import java.util.List;/*** SignAdapter* Created by E.M on 2016/4/21.*/public class SignAdapter extends CalendarAdapter {private List<SignEntity> data;public SignAdapter(List<SignEntity> data) {this.data = data;}@Overridepublic SignView.DayType getType(int dayOfMonth) {return SignView.DayType.valueOf(data.get(dayOfMonth - 1).getDayType());}}5.⾃定义viewpackage com.example.myapplication30;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Color;import poseShader;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff;import android.graphics.RadialGradient;import android.graphics.Rect;import android.graphics.Shader;import android.graphics.SweepGradient;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import java.util.Calendar;/*** 签到⽇历控件* Created by E.M on 2016/4/20.*/public class SignView extends View {private static final String[] WEEK_MARK = {"⼀", "⼆", "三", "四", "五", "六", "⽇"}; private static final int MAX_COLUMN = 7;/*** 周内*/private static final int COLOR_MARKER_WEEKDAY = 0xFF999999;private static final int COLOR_MARKER_WEEKEND = 0xFF1B89CD;/*** 已签到背景⾊*///private static final int COLOR_BACKGROUND_HIGHLIGHT = 0xFFFF0000;/*** 未签到背景⾊*/private static final int COLOR_BACKGROUND_NORMAL = 0xFF9C9C9C;/*** 等待签到背景⾊*/private static final int COLOR_BACKGROUND_WAIT = 0xFFFE7471;/*** 已签到⽂字颜⾊*/private static final int COLOR_TEXT_HIGHLIGHT = 0xFFFFFFFF;/*** 未签到⽂字颜⾊*/private static final int COLOR_TEXT_NORMAL = 0xFF606060;// /**// * 不可⽤⽂字颜⾊// */// private static final int COLOR_TEXT_DISABLED = 0xFFD4D4D4;private static final int MARKER_TEXT_SIZE = 40;private static final int CELL_TEXT_SIZE = 40;private static final int VERTICAL_SPACE = 51;private static final int VERTICAL_MARGIN = 62;private static final int HORIZONTAL_MARGIN = 39;private static final int CELL_SIZE = 80;private static final int WAIT_LINE_SIZE = 14;private int dayOfMonthToday;private int markerTextY;private int verticalCellTop;private int sumDayOfMonth;private int daysOfFirstWeek;private int horizontalSpace;private int deltaTextCellY;private int deltaTextMarkerY;private int verticalSpace;private int verticalMargin;private int horizontalMargin;private int cellSize;private int waitLineSize;private Path waitPath;private Rect waitRect;private Paint paintWeekday;private Paint paintWeekend;private Paint paintTextNormal;private Paint paintTextHighlight;private Paint paintBackgroundWait;private Paint paintBackgroundNormal;private Paint paintBackgroundHighlight;private CalendarAdapter adapter;public SignView(Context context) {this(context, null);}public SignView(Context context, AttributeSet attrs) {this(context, attrs, -1);}public SignView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initResolution();initPaint();initData();}private void initResolution() {// resolutionUtil = ResolutionUtil.getInstance();// verticalSpace = resolutionUtil.formatVertical(VERTICAL_SPACE);// verticalMargin = resolutionUtil.formatVertical(VERTICAL_MARGIN);// horizontalMargin = resolutionUtil.formatHorizontal(HORIZONTAL_MARGIN); // cellSize = resolutionUtil.formatVertical(CELL_SIZE);// waitLineSize = resolutionUtil.formatVertical(WAIT_LINE_SIZE);verticalSpace = VERTICAL_SPACE;verticalMargin = VERTICAL_MARGIN;horizontalMargin = HORIZONTAL_MARGIN;cellSize = CELL_SIZE;waitLineSize = WAIT_LINE_SIZE;}private void initPaint() {// int markerTextSize = resolutionUtil.formatVertical(MARKER_TEXT_SIZE); // int cellTextSize = resolutionUtil.formatVertical(CELL_TEXT_SIZE);int markerTextSize = MARKER_TEXT_SIZE;int cellTextSize = CELL_TEXT_SIZE;paintWeekday = new Paint();paintWeekday.setAntiAlias(true);paintWeekday.setColor(COLOR_MARKER_WEEKDAY);paintWeekday.setTextSize(markerTextSize);paintWeekday.setTextAlign(Paint.Align.CENTER);paintWeekend = new Paint();paintWeekend.setAntiAlias(true);paintWeekend.setColor(COLOR_MARKER_WEEKEND);paintWeekend.setTextSize(markerTextSize);paintWeekend.setTextAlign(Paint.Align.CENTER);paintTextNormal = new Paint();paintTextNormal.setAntiAlias(true);paintTextNormal.setColor(COLOR_TEXT_NORMAL);paintTextNormal.setTextSize(cellTextSize);paintTextNormal.setTextAlign(Paint.Align.CENTER);paintTextHighlight = new Paint();paintTextHighlight.setAntiAlias(true);paintTextHighlight.setColor(COLOR_TEXT_HIGHLIGHT);paintTextHighlight.setTextSize(cellTextSize);paintTextHighlight.setTextAlign(Paint.Align.CENTER);paintBackgroundWait = new Paint();paintBackgroundWait.setAntiAlias(true);paintBackgroundWait.setColor(COLOR_BACKGROUND_WAIT);paintBackgroundWait.setStrokeWidth(2);paintBackgroundWait.setStyle(Paint.Style.STROKE);paintBackgroundNormal = new Paint();paintBackgroundNormal.setAntiAlias(true);paintBackgroundNormal.setColor(COLOR_BACKGROUND_NORMAL);paintBackgroundNormal.setStrokeWidth(2);paintBackgroundNormal.setStyle(Paint.Style.STROKE);paintBackgroundHighlight = new Paint();paintBackgroundHighlight.setAntiAlias(true);paintBackgroundHighlight.setStrokeWidth(2);paintBackgroundHighlight.setStyle(Paint.Style.FILL);//颜⾊//paintBackgroundHighlight.setColor(COLOR_BACKGROUND_HIGHLIGHT);//多种颜⾊数组//int[] colors = {Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW,Color.MAGENTA};//float[] position = {0f, 0.2f, 0.4f, 0.6f, 1.0f};//Shader shader1 = new LinearGradient(100,850,600,850,colors,position,Shader.TileMode.CLAMP);//paintBackgroundHighlight.setShader(shader1);//设置背景图⽚/* Bitmap placeholder = BitmapFactory.decodeResource(getResources(), R.mipmap.small);Shader shader1 = new BitmapShader(placeholder, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); paintBackgroundHighlight.setShader(shader1);*/}private void initData() {Paint.FontMetricsInt fmiMarker = paintWeekday.getFontMetricsInt();deltaTextMarkerY = -(fmiMarker.bottom - fmiMarker.top) / 2 - fmiMarker.top;markerTextY = verticalMargin + cellSize / 2;Paint.FontMetricsInt fmiCell = paintTextNormal.getFontMetricsInt();deltaTextCellY = -(fmiCell.bottom - fmiCell.top) / 2 - fmiCell.top;verticalCellTop = verticalMargin + cellSize;Calendar calendarToday = Calendar.getInstance();dayOfMonthToday = calendarToday.get(Calendar.DAY_OF_MONTH);int dayOfWeek;sumDayOfMonth = calendarToday.getActualMaximum(Calendar.DAY_OF_MONTH);Calendar calendarFirstDay = Calendar.getInstance();calendarFirstDay.set(Calendar.DAY_OF_MONTH, 1);dayOfWeek = calendarFirstDay.get(Calendar.DAY_OF_WEEK);if (dayOfWeek == Calendar.SUNDAY) {dayOfWeek = 7;} else {dayOfWeek = dayOfWeek - 1;}daysOfFirstWeek = MAX_COLUMN - dayOfWeek + 1;}private void createWaitBackground(int topX, int topY) {waitPath = new Path();waitPath.moveTo(topX, topY + waitLineSize);waitPath.lineTo(topX, topY);waitPath.lineTo(topX + waitLineSize, topY);waitPath.moveTo(topX + cellSize - waitLineSize, topY + cellSize);waitPath.lineTo(topX + cellSize, topY + cellSize);waitPath.lineTo(topX + cellSize, topY + cellSize - waitLineSize);waitRect = new Rect(topX, topY, topX + cellSize, topY + cellSize);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);horizontalSpace = (w - MAX_COLUMN * cellSize - horizontalMargin * 2) / (MAX_COLUMN - 1);}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);drawWeekMark(canvas);drawCellsBackground(canvas);drawCells(canvas);}private void drawWeekMark(Canvas canvas) {int y = markerTextY + deltaTextMarkerY;for (int i = 0; i < 7; i++) {int x = horizontalMargin + i * (horizontalSpace + cellSize)+ cellSize / 2;if (i < 5) {canvas.drawText(WEEK_MARK[i], x, y, paintWeekday);} else {canvas.drawText(WEEK_MARK[i], x, y, paintWeekend);}}}private void drawCellsBackground(Canvas canvas) {for (int i = 1; i <= dayOfMonthToday; i++) {drawCellBackground(canvas, i, getColumnIndex(i), getRowIndex(i));}}/*** 根据⾏列序号绘制⽇期背景** @param canvas 画布* @param dayOfMonth ⽇期* @param column 列序号* @param row ⾏序号*/private void drawCellBackground(Canvas canvas, int dayOfMonth, int column, int row) {int x = horizontalMargin + column * (horizontalSpace + cellSize)+ cellSize / 2;int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2;if (adapter != null) {DayType dayType = adapter.getType(dayOfMonth);switch (dayType) {case WAITING:if (waitPath == null) {createWaitBackground(x - cellSize / 2, y - cellSize / 2);}canvas.drawPath(waitPath, paintBackgroundWait);break;case SIGNED:// canvas.drawCircle(x, y, cellSize/2, paintBackgroundHighlight);// canvas.drawRect(x - 60, y - 60, x + 60, y + 60, paintBackgroundHighlight);// 正⽅形// Bitmap placeholder = BitmapFactory.decodeResource(getResources(), R.mipmap.purtest);// canvas.drawBitmap(placeholder,);wCircle(x, y, cellSize/2, paintBackgroundHighlight);canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.small3),x-40, y-40 , paintBackgroundHighlight); break;default:canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);break;}} else {canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);}}private void drawCells(Canvas canvas) {for (int i = 1; i <= sumDayOfMonth; i++) {drawCell(canvas, i, getColumnIndex(i), getRowIndex(i));}}/*** 根据⾏列序号绘制⽇期** @param canvas 画布* @param dayOfMonth ⽇期* @param column 列序号* @param row ⾏序号*/private void drawCell(Canvas canvas, int dayOfMonth, int column, int row) {int x = horizontalMargin + column * (horizontalSpace + cellSize)+ cellSize / 2;int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2+ deltaTextCellY;if (adapter != null && dayOfMonth <= dayOfMonthToday) {DayType dayType = adapter.getType(dayOfMonth);Paint paint;switch (dayType) {case SIGNED:paint = paintTextHighlight;break;default:paint = paintTextNormal;break;}canvas.drawText(String.valueOf(dayOfMonth), x, y, paint);} else {canvas.drawText(String.valueOf(dayOfMonth), x, y, paintTextNormal);}}/*** 获取列序号** @param dayOfMonth ⽇期* @return 列序号*/private int getColumnIndex(int dayOfMonth) {Calendar calendar = Calendar.getInstance();calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);if (dayOfWeek == Calendar.SUNDAY) {dayOfWeek = 6;} else {dayOfWeek = dayOfWeek - 2;}return dayOfWeek;}/*** 获取⾏序号** @param dayOfMonth ⽇期* @return ⾏序号*/private int getRowIndex(int dayOfMonth) {float weight = (dayOfMonth - daysOfFirstWeek) / (MAX_COLUMN * 1f);double rowIndexDouble = Math.abs(Math.ceil(weight));return (int) rowIndexDouble;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_UP) {float x = event.getX();float y = event.getY();if (waitPath != null) {if (adapter.getType(dayOfMonthToday).equals(DayType.WAITING)) {if (x >= waitRect.left && y >= waitRect.top && x <= waitRect.right && y <= waitRect.bottom) { if (onTodayClickListener != null) {onTodayClickListener.onTodayClick();}}}}}return true;}public void setAdapter(CalendarAdapter adapter) {this.adapter = adapter;this.invalidate();}public int getDayOfMonthToday() {return dayOfMonthToday;}public void notifyDataSetChanged() {invalidate();}private OnTodayClickListener onTodayClickListener;public void setOnTodayClickListener(OnTodayClickListener onTodayClickListener) {this.onTodayClickListener = onTodayClickListener;}public interface OnTodayClickListener {void onTodayClick();}public enum DayType {/*** 已签到状态,时间已过*/SIGNED(0),/*** 未签到状态,时间已过*/UNSIGNED(1),/*** 等待状态,即当⽇还未签到*/WAITING(2),/*** 不可达到状态,未到时间*/UNREACHABLE(3),/*** 不可⽤状态,⾮当前⽉份*/DISABLED(4);private int value;DayType(int value) {this.value = value;}public int getValue() {return value;}public static DayType valueOf(int value) {switch (value) {case 0:return SIGNED;case 1:return UNSIGNED;case 2:return WAITING;case 3:return UNREACHABLE;case 4:return DISABLED;default:return DISABLED;}}}}以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
Android自定义View实现绘制虚线的方法详解
Android⾃定义View实现绘制虚线的⽅法详解前⾔说实话当第⼀次看到这个需求的时候,第⼀反应就是Canvas只有drawLine⽅法,并没有drawDashLine⽅法啊!这咋整啊,难道要我⾃⼰做个遍历不断的drawLine?不到1秒,我就放弃这个想法了,因为太恶⼼了。
⽅法肯定是有的,只不过我不知道⽽已。
绘制⽅法最简单的⽅法是利⽤ShapeDrawable,⽐如说你想⽤虚线要隔开两个控件,就可以在这两个控件中加个View,然后给它个虚线背景。
嗯,理论上就是这样⼦的,实现上也很简单。
<!-- drawable ⽂件 --><?xml version="1.0" encoding="utf-8"?><shape xmlns:android="/apk/res/android"android:shape="line"><strokeandroid:width="1dp"android:color="@color/dash_line"android:dashGap="2dp"android:dashWidth="3dp"/></shape><!-- 布局⽂件 --><?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="/apk/res/android"xmlns:tools="/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:gravity="center"tools:context="hope.example.dashlinedemo.MainActivity"><TextViewandroid:layout_width="match_parent"android:layout_height="40dp"android:gravity="center"android:text="分享给微信好友"/><Viewandroid:layout_width="match_parent"android:layout_height="2dp"android:background="@drawable/dash_line" /><TextViewandroid:layout_width="match_parent"android:layout_height="40dp"android:gravity="center"android:text="分享⾄朋友圈" /></LinearLayout>写完之后,从Android Studio的预览功能上就可以看到效果了。
VMware View 自定义View Client 登录方式(二)
利用Windows用户自动登录View虚拟桌面接下来我们来实现使用Windows登录用户自动登录虚拟桌面的功能,同样我们还是通过修改组策略的方式来实现。
首先打开我们在上面创建的“View User GPO”这个组策略,添加View相关的组策略模板(模板文件可以在View服务器安装目录中找到,默认目录为:c:\Program Files\VMware\VMware View\Server\extras\GroupPolicyFiles\)添加完成之后,我们可以看到在组策略-用户配置中增加了相关View的策略设置。
打开组策略—用户配置—VMware View Client Configuration – Scripting definitions ,如果我们需要实现自动利用Windows 登录用户登录虚拟桌面,在这里只需要指定View 服务器的地址即可,也就是修改策略项“Server URL” ,并填入正确的View 服务器地址,保存即可。
客户端更新完组策略之后,我们来看看效果:如下图,当我们登录Windows之后,View Client 自动连接服务器,并将用户可用的桌面池列出来,从而实现了自动使用Windows凭证登录虚拟桌面。
我们再后头看看之前的组策略,在View Client的组策略中还提供了很多设置,包括登录的桌面名称,当配置了这个设置,View Client 将直接登录虚拟桌面。
下表是部分View组策略的说明:三、其他定制View Client登录的设置在实际用户环境中,往往还存在一些其他的定制要求,像Windows系统自动登录,关闭CTRL+ALT+DELETE 选项中的功能,以及自动连接虚拟桌面等等,接下来我们就来介绍一下实现这些要求的方法。
3.1 Windows系统自动登录实现Windows系统自动登录,主要用于简化桌面用户登录,通过修改Windows用户登录方式,让Windows自动使用管理员预先设定的用户名密码登录系统。
Android自定义水平或垂直虚线效果
Android⾃定义⽔平或垂直虚线效果项⽬中有时候会⽤到虚线,怎么办?drawable下创建⼀个shape类型的xml⽂件绘制,然后引⽤到view的background下?如果⽤到虚线的地⽅很多呢?创建多个,分别引⽤?横向的还好说,竖向的呢?垂直的虚线,普通的创建是显⽰不出来的,如果需要,就要进⾏旋转等的操作。
但是,还是那个问题,需要很多个怎么办?挨个创建?完全没必要,写个⾃定义,对外暴露设置虚线属性的⽅法就⾏。
源码如下:最后的说明很重要最后的说明很重要最后的说明很重要效果图:源码:ImaginaryLineViewpackage com.chen.demo;import android.content.Context;import android.graphics.Canvas;import android.graphics.DashPathEffect;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PathEffect;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;/*** ⾃定义垂直虚线view* chenjianqiang* 2017/6/14* <p>* 使⽤⽅法:* 在代码中findview之后,调⽤setLineAttribute⽅法,⾃定义虚线颜⾊及宽度*/public class ImaginaryLineView extends View {private Context ct;private Paint mPaint;private Path mPath;private PathEffect effects;private int width;private int height;private int defaultColor=0xffff0000;public ImaginaryLineView(Context context) {this(context, null);}public ImaginaryLineView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, -1);}public ImaginaryLineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);ct = context;init();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);width = w;height = h;}private void init() {//初始化,并打开抗锯齿mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.STROKE);mPaint.setColor(defaultColor);mPaint.setStrokeWidth(dip2px(ct, 1));mPath = new Path();//数组含义:⾥⾯最少要有2个值,值的个数必须是偶数个。
Android 自定义View消除锯齿实现图片旋转,添加边框及文字说明
先看看图片的效果,左边是原图,右边是旋转之后的图;之所以把这个写出来是因为在一个项目中需要用到这样的效果,我试过用FrameLayout 布局如上的画面,然后旋转FrameLayout,随之而来也就存在了一些问题——锯齿!在网上搜索之后,有两种方法,一是利用Paint,二是利用Canvas;(1)、paint.setAntiAlias(true);paint.setFlags(Paint.ANTI_ALIAS_FLAG);(2)、DrawFilter pfdf = new PaintFlagsDrawFilter(0,Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);canvas.setDrawFilter(pfdf);而如果利用paint,或者canvas,需要从哪获取paint/canvas,这也是一个问题;在实现的过程中,尝试过自定义FrameLayout下面的单个View{ImageView,TextView},但都以失败告终,失败的主要问题在于右图下边的文字描述无法和相片边框相对齐,而且用Matrix旋转背景之后背景大小改变,位置也不在最下边,所以就采用了单独实现一个View的方法,主要原因还是因为自身对Canvas绘图及Paint画笔不是很熟悉,所以导致的效率不高;public class RotateTextImageView extends View {PaintFlagsDrawFilter pfdf;Paint paint;Matrix matrix;Bitmap bitmap;int index = -1;private int oriHeight;private int oriWidth;private int newHeight;private int newWidth;private int angle = 5;protected Path path = new Path();private float[] f = new float[8];private int shawHeight = 20;private int borderSize = 8;Bitmap oriBitmap;private String text = "";public RotateTextImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);initCanvasInfo();}public RotateTextImageView(Context context, AttributeSet attrs) { super(context, attrs);initCanvasInfo();}public RotateTextImageView(Context context) {super(context);initCanvasInfo();}/*** 初始化Paint*/protected void initCanvasInfo() {pfdf = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG| Paint.FILTER_BITMAP_FLAG);paint = new Paint();paint.setAntiAlias(true);matrix = new Matrix();matrix.setRotate(5);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);paint.reset();// 消除锯齿paint.setAntiAlias(true);paint.setFlags(Paint.ANTI_ALIAS_FLAG);canvas.setDrawFilter(pfdf);canvas.drawBitmap(bitmap, 0, 0, paint);newHeight = bitmap.getHeight();newWidth = bitmap.getWidth();calculatePoints();// 添加阴影path.reset();path.moveTo(f[0], f[1]);path.lineTo(f[2], f[3]);path.lineTo(f[4], f[5]);path.lineTo(f[6], f[7]);path.close();paint.setStyle(Paint.Style.FILL_AND_STROKE);paint.setColor(Color.parseColor("#96ffffff"));canvas.drawPath(path, paint);// 添加字符if (text != null && !text.equals("")) {path.reset();paint.setTextSize(18);float width = paint.measureText(text);path.moveTo((f[0] + f[2]) / 2, (f[1] + f[3]) / 2);path.lineTo((f[4] + f[6]) / 2, (f[5] + f[7]) / 2);paint.setColor(Color.parseColor("#2b2b2b"));canvas.drawTextOnPath(text, path, (oriWidth - width) / 2, 3, paint);}layout(0, 0, newWidth, newHeight);}/*** 计算坐标值*/private void calculatePoints() {double a = angle * Math.PI / 180;BigDecimal height = new BigDecimal(oriHeight);BigDecimal width = new BigDecimal(oriWidth);BigDecimal cos = new BigDecimal(Math.cos(a));BigDecimal tan = new BigDecimal(Math.tan(a));f[0] = 0;f[1] = height.multiply(cos).floatValue();f[2] = tan.multiply(new BigDecimal(shawHeight)).floatValue();f[3] = (new BigDecimal(f[1])).subtract(new BigDecimal(shawHeight)) .floatValue();f[4] = width.multiply(cos).add(new BigDecimal(f[2])).floatValue();f[5] = new BigDecimal(newHeight - shawHeight).floatValue();f[6] = width.multiply(cos).floatValue();f[7] = new BigDecimal(newHeight).floatValue();}/*** 设置图片** @param bmp*/public void setBitmap(Bitmap bmp) {oriBitmap = bmp;matrix.reset();matrix.setRotate(angle);Bitmap bitmapF = addFrame(bmp);oriHeight = bitmapF.getHeight();oriWidth = bitmapF.getWidth();bitmap = Bitmap.createBitmap(bitmapF, 0, 0, bitmapF.getWidth(),bitmapF.getHeight(), matrix, true);postInvalidate();}/*** 旋转角度** @param angle*/public void setAngle(int angle) {this.angle = angle;setBitmap(oriBitmap);}/*** 设置底部阴影高度** @param shawHeight*/public void setShawHeight(int shawHeight) {this.shawHeight = shawHeight;postInvalidate();}/*** 生成添加了白色边缘的图** @param bmp* @return*/protected Bitmap addFrame(Bitmap bmp) {Bitmap bmpWithBorder = Bitmap.createBitmap(bmp.getWidth() + borderSize* 2, bmp.getHeight() + borderSize * 2, bmp.getConfig());Canvas canvas = new Canvas(bmpWithBorder);canvas.drawColor(Color.WHITE);canvas.drawBitmap(bmp, borderSize, borderSize, null);return bmpWithBorder;}/*** 设置字符串** @param text*/public void setText(String text) {this.text = text;postInvalidate();}/*** 获取字体高度*/protected int getFontHeight() {FontMetrics fm = paint.getFontMetrics();return (int) Math.ceil(fm.descent - fm.top) + 2;}}代码解释:其实没有什么难的东西,只是一些数学运算,代码中每一个方法都有对应的功能注释。
自定义view的绘制流程
自定义view的绘制流程自定义View 的绘制流程如下:1. 构造函数和测量阶段:在构造函数中,会初始化一些变量和设置画笔等绘制相关的属性。
然后系统会调用onMeasure() 方法来测量View 的宽高。
在该方法中,可以利用MeasureSpec 类来获取父容器传递给子View 的测量规格,然后根据测量规格来设置自己的宽高。
2. 布局阶段:当父容器决定子View 的大小和位置时,会调用onLayout() 方法来确定子View 的位置。
在该方法中,可以通过getMeasuredWidth() 和getMeasuredHeight() 方法获取View 的测量宽高,在确定子View 的位置时,通常需要使用到这些值。
3. 绘制阶段:当确定了子View 的位置后,系统会调用onDraw() 方法来绘制View。
在该方法中,可以使用Canvas 对象来进行绘制操作,如画线、画圆、画文字等。
还可以使用Paint 对象来设置绘制的颜色、线条粗细等属性。
4. 触摸事件处理:当用户触摸屏幕时,系统会将触摸事件传递给View,并调用相应的事件处理方法,如onTouchEvent() 方法。
在该方法中,可以根据具体需求来处理触摸事件,如判断点击位置、处理拖动等操作。
5. 其他回调方法:除了上述的关键方法外,还可以重写一些其他回调方法,如onAttachedToWindow()、onDetachedFromWindow()、onSizeChanged() 等,用于在特定时机进行相应的操作,如监听View 的attach/detach 事件、在View 大小发生变化时做出响应等。
总体来说,自定义View 的绘制流程通常包括构造函数和测量阶段、布局阶段、绘制阶段、触摸事件处理和其他回调方法。
在每个阶段中,可以根据需求来实现相应的逻辑,从而实现自定义的绘制效果。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.自定义view的分类:
2.具体介绍&使用场景
3.使用注意点:
3.1支持特殊属性:
支持wrap_content
如果不在onMeasure()中对wrap_content作特殊处理,那么wrap_content属性将失效支持padding&margin
如果不支持,那么padding和margin(ViewGroup情况) 的属性将失效
--对于继承View的控件,padding是在draw()中处理的
--对于继承ViewGroup的控件,padding和margin会直接影响measure和layout过程3.2多线程应直接使用post方式
View的内部本身提供了post系列的方法,完全可以替代Handler的作用,使用起来更加方便,直接
3.3避免内存泄漏:
主要针对view中含有线程或动画的情况:当view退出或不可见时,记得及时停止该view包含的线程和动画,否则会造成内存泄漏问题。
启动或停止线程/动画的方式:
a:启动线程/动画:使用view.onAttachedToWindow(),因为该方法调用的时机是当包含view的activity启动的时刻。
b:停止线程/动画:使用view.onDetachedFromWindow(),因为该方法调用的时机是当包含view的activity退出或当前view被remove的时刻
3.4处理好滑动冲突:
当view带有滑动嵌套情况时,必须要处理好滑动冲突,否则会严重影响view的显示效果4.具体实例:
4.1继承View的介绍
4.2具体步骤:
a:创建自定义view类(继承View类)
b:布局文件添加自定义View组件
c:注意点设置(处理wrap_content&padding属性自定义属性等)
4.3
a:手动支持wrap_content属性
wrap_content 视图的宽高被设定成刚好适应视图的最小尺寸
match_parent 视图的宽高延伸至充满整个父布局
解决方案:
绘制时考虑传入的padding属性值(四个方向)
在自定义view类的复写onDraw()进行设置
b:支持padding属性
padding属性:用于设置控件内容相对控件边缘的边距区别与margin属性:控件边缘相对于父控件的边缘。
c:提供自定义属性:
使用步骤:
在values目录下创建自定义属性的xml文件
在自定义view的构造方法中解析自定义属性的值 在布局文件中使用自定义属性。