Silverlight自定义控件开发:温度计

由于在实际项目中需要实时显示采集到的空气温湿度,土壤温湿度值,需要用比较显眼并且清楚明了的方式来展示,这里我们准备采用温度计的方式来进行。一方面是因为大家都熟悉这个,知道怎么去看;同时,温度计本身也比较好封装。以下就是封装好的效果及其调用代码(水银柱和刻度线都是有动画效果的,看上去比较逼真):

调用代码如下:

   1:              var data = new DataNotify();
   2:              data.MaxData = 30;
   3:              data.MinData = -15;
   4:   
   5:              data.MinRange = -15;
   6:              data.MaxRange = 75;
   7:   
   8:              data.CurrentData = 40;
   9:   
  10:              data.Title = "空气温度";
  11:              data.Unit = "℃";
  12:              data.ThemeSet = Theme.Red;
  13:   
  14:              var uc = new TemperatureControl(data);
  15:              uc.Margin = new Thickness(-620, 0, 10, 10);
  16:              Test.Children.Add(uc);
  17:   
  18:   
  19:              var data1 = new DataNotify();
  20:              data1.MaxData = 100;
  21:              data1.MinData = 0;
  22:              data1.MinRange = 0;
  23:              data1.MaxRange = 100;
  24:              data1.CurrentData = 83;
  25:              data1.Title = "空气湿度";
  26:              data1.Unit = "%";
  27:              data1.ThemeSet = Theme.Blue;
  28:   
  29:              var uc1 = new TemperatureControl(data1);
  30:              uc1.Margin = new Thickness(-207, 0, 10, 10);
  31:              Test.Children.Add(uc1);
  32:   
  33:              var data2 = new DataNotify();
  34:              data2.MaxData = 60;
  35:              data2.MinData = -10;
  36:              data2.MinRange = -10;
  37:              data2.MaxRange = 100;
  38:              data2.CurrentData = 36;
  39:              data2.Title = "土壤温度";
  40:              data2.Unit = "℃";
  41:              data2.ThemeSet = Theme.Orange;
  42:   
  43:              var uc2 = new TemperatureControl(data2);
  44:              uc2.Margin = new Thickness(213, 0, 10, 10);
  45:              Test.Children.Add(uc2);
  46:   
  47:              var data3 = new DataNotify();
  48:              data3.MaxData = 60;
  49:              data3.MinData = -10;
  50:              data3.MinRange = -10;
  51:              data3.MaxRange = 100;
  52:              data3.CurrentData = 36;
  53:              data3.Title = "土壤湿度";
  54:              data3.Unit = "%";
  55:              data3.ThemeSet = Theme.Mo;
  56:   
  57:              var uc3 = new TemperatureControl(data3);
  58:              uc3.Margin = new Thickness(633, 0, 10, 10);
  59:              Test.Children.Add(uc3);

由于调用代码相当简单,我就不再这里赘述了,下面着重讲解其实现方式。

首先在Silverlight项目中创建一个用户控件,我们命名为“TemperatureControl.xaml”,然后创建一个“ResData.xaml”资源文件项目,用于放置样式定义。

然后开始对项目进行布局,组织xaml代码,得到的页面显示如下:

XAML文件代码如下:

   1:  <UserControl
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:      mc:Ignorable="d"
   7:      x:Class="TinyFrame.Silverlight.TemperatureControl"
   8:      Loaded="UserControl_Loaded"
   9:      Width="200" Height="227">
  10:   
  11:      <Border Style="{StaticResource GridBorder}">
  12:          <Grid x:Name="LayoutRoot" Background="White">
  13:              <Grid.RowDefinitions>
  14:              <RowDefinition Height="30"/>
  15:              <RowDefinition/>
  16:              </Grid.RowDefinitions>
  17:              <Border Grid.Row="0" Style="{StaticResource TopBorder}">
  18:                  <TextBlock Style="{StaticResource TxtTitle}" Text="{Binding Title}" />
  19:              </Border>
  20:              <Border Grid.Row="1">
  21:                  <Grid>
  22:                      <Grid.ColumnDefinitions>
  23:                          <ColumnDefinition Width="40"/>
  24:                          <ColumnDefinition/>
  25:                          <ColumnDefinition/>
  26:                      </Grid.ColumnDefinitions>
  27:                      <Border Name="bRange" Style="{StaticResource RangeBorder}" BorderBrush="{Binding LineColor}"  VerticalAlignment="Bottom">
  28:                      <Grid>
  29:                          <Grid.RowDefinitions>
  30:                              <RowDefinition/>
  31:                              <RowDefinition/>
  32:                          </Grid.RowDefinitions>
  33:                              <TextBlock Grid.Row="0" Style="{StaticResource TxtCommon}" Name="txtMax" Foreground="{Binding lineColor}" VerticalAlignment="Top" ></TextBlock>
  34:                              <TextBlock Grid.Row="1" Style="{StaticResource TxtCommon}" Name="txtMin" Foreground="{Binding lineColor}" VerticalAlignment="Bottom"></TextBlock>
  35:                      </Grid>
  36:                      </Border>
  37:                      <Image Margin="-14,18,0,0" Source="{Binding BgImg}"  Grid.Column="1"/>
  38:                      <Border Grid.Column="2">
  39:                      <Grid>
  40:                      <TextBlock Margin="20,40,6,0" Text="{Binding CurrentData}" Foreground="{Binding IsOK}" Style="{StaticResource TxtValue}" ></TextBlock>
  41:                      <TextBlock Margin="20,65,6,0" Text="{Binding MaxData}"  Style="{StaticResource TxtValue}"></TextBlock>
  42:                      <TextBlock Margin="20,90,6,0" Text="{Binding MinData}"  Style="{StaticResource TxtValue}"></TextBlock>
  43:                      <TextBlock Style="{StaticResource TxtIntro}" Text="当前值:" Margin="-20,40,0,0" Name="i1"  />
  44:                      <TextBlock Style="{StaticResource TxtIntro}" Text="最大值:" Margin="-20,65,0,0"  Name="i2" />
  45:                      <TextBlock Style="{StaticResource TxtIntro}" Text="最小值:" Margin="-20,90,0,0"  Name="i3" />
  46:                          </Grid>
  47:                      </Border>
  48:                      <Border Style="{StaticResource BorderUnit}" Grid.Column="1" Margin="22,24,0,0">
  49:                      <TextBlock   Name="t" Text="{Binding Unit}"  />
  50:                      </Border>
  51:                      <Image x:Name="bStep" Style="{StaticResource ImgStep}" Source="{Binding BgStep}" Margin="11,0,0,27" Grid.Column="1" />
  52:                      <Canvas Height="120" Background="White" HorizontalAlignment="Left" Margin="24,47,0,0" Name="canvas1" VerticalAlignment="Top" Width="32" OpacityMask="Black" Grid.Column="1" />
  53:                  </Grid>
  54:              </Border>
  55:          </Grid>
  56:      </Border>
  57:  </UserControl>

通过第一副图的对比,我们能轻易的知道上图中那些空白的位置放什么东西。

由于在XAML中我采用了Binding来进行数据绑定,那么就展示下我用于数据绑定的类:

   1:   public class DataNotify : INotifyPropertyChanged
   2:      {
   3:          private double minData = 0;  //最小值
   4:          public double MinData
   5:          {
   6:              get
   7:              {
   8:                  return minData;
   9:              }
  10:              set
  11:              {
  12:                  if (value != minData)
  13:                  {
  14:                      minData = value;
  15:                      Notify("MinData");
  16:                  }
  17:              }
  18:          }
  19:   
  20:          private double maxData = 1;  //最大值
  21:          public double MaxData
  22:          {
  23:              get
  24:              {
  25:                  return maxData;
  26:              }
  27:              set
  28:              {
  29:                  if (value != maxData)
  30:                  {
  31:                      maxData = value;
  32:                      Notify("MaxData");
  33:                  }
  34:              }
  35:          }
  36:   
  37:          private double minRange = 0;  //刻度最小范围
  38:          public double MinRange
  39:          {
  40:              get { return minRange; }
  41:              set
  42:              {
  43:                  if (minRange != value)
  44:                  {
  45:                      minRange = value;
  46:                      Notify("MinRange");
  47:                  }
  48:              }
  49:          }
  50:   
  51:          private double maxRange = 90; //刻度最大范围
  52:          public double MaxRange
  53:          {
  54:              get { return maxRange; }
  55:              set
  56:              {
  57:                  if (maxRange != value)
  58:                  {
  59:                      maxRange = value;
  60:                      Notify("MaxRange");
  61:                  }
  62:              }
  63:          }
  64:   
  65:          private double currentData;  //当前值
  66:          public double CurrentData
  67:          {
  68:              get
  69:              {
  70:                  return currentData;
  71:              }
  72:              set
  73:              {
  74:                  if (value != currentData)
  75:                  {
  76:                      currentData = value;
  77:                      Notify("CurrentData");
  78:                  }
  79:              }
  80:          }
  81:   
  82:          private Brush isOK;   //指标是否正常
  83:          public Brush IsOK
  84:          {
  85:              get
  86:              {
  87:                  if (currentData >= minData && currentData <= maxData)
  88:                      return new SolidColorBrush(Colors.Black);
  89:                  else
  90:                      return new SolidColorBrush(Colors.Red);
  91:              }
  92:          }
  93:   
  94:          private string title;  //标题
  95:          public string Title
  96:          {
  97:              get
  98:              {
  99:                  return title;
 100:              }
 101:              set
 102:              {
 103:                  if (value != title)
 104:                  {
 105:                      title = value;
 106:                      Notify("Title");
 107:                  }
 108:              }
 109:          }
 110:   
 111:          private string unit;   //单位
 112:          public string Unit
 113:          {
 114:              get
 115:              {
 116:                  return unit;
 117:   
 118:              }
 119:              set
 120:              {
 121:                  if (value != unit)
 122:                  {
 123:                      unit = value;
 124:                      Notify("Unit");
 125:                  }
 126:              }
 127:          }
 128:   
 129:          private Theme themeSet;
 130:          public Theme ThemeSet
 131:          {
 132:              get
 133:              {
 134:                  return themeSet;
 135:              }
 136:              set
 137:              {
 138:                  if (value != themeSet)
 139:                  {
 140:                      themeSet = value;
 141:                      Notify("ThemeSet");
 142:                  }
 143:              }
 144:          }
 145:   
 146:          private string bgImg;  //背景默认值
 147:          public string BgImg
 148:          {
 149:              get
 150:              {
 151:                  return bgImg;
 152:              }
 153:              set
 154:              {
 155:                  if (value != bgImg)
 156:                  {
 157:                      bgImg = value;
 158:                      Notify("BgImg");
 159:                  }
 160:              }
 161:          }
 162:   
 163:          private string bgStep;  //水银柱块默认值
 164:          public string BgStep
 165:          {
 166:              get
 167:              {
 168:                  return bgStep;
 169:              }
 170:              set
 171:              {
 172:                  if (value != bgStep)
 173:                  {
 174:                      bgStep = value;
 175:                      Notify("BgStep");
 176:                  }
 177:              }
 178:          }
 179:   
 180:          private string lineColor;  //刻度范围条的颜色
 181:          public string LineColor
 182:          {
 183:              get
 184:              {
 185:                  return lineColor;
 186:              }
 187:              set
 188:              {
 189:                  if (lineColor != value)
 190:                  {
 191:                      lineColor = value;
 192:                      Notify("LineColor");
 193:                  }
 194:              }
 195:          }
 196:   
 197:          public event PropertyChangedEventHandler PropertyChanged;
 198:   
 199:          public void Notify(string propertyName)
 200:          {
 201:              if (PropertyChanged != null)
 202:                  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 203:          }
 204:      }

上面的代码中,封装了温度计的所有需要的属性,包括刻度值,最大最小范围值,当前值,主题,标题,指标正常与否等。

最后就是具体的事件组织方法:

   1:   public partial class TemperatureControl : UserControl
   2:      {
   3:          public TemperatureControl(DataNotify dataNotify)
   4:          {
   5:              InitializeComponent();
   6:              this.dataNotify = dataNotify;
   7:   
   8:              //绑定数据上下文
   9:              this.DataContext = dataNotify;
  10:   
  11:              //左侧刻度线的最大值最小值
  12:              double tempMinData = dataNotify.MinData;
  13:              if (timer == null)
  14:                  timer = new DispatcherTimer();
  15:              timer.Interval = new TimeSpan(100);
  16:              timer.Tick += (sender, e) =>
  17:              {
  18:                  tempMinData++;
  19:                  bRange.Dispatcher.BeginInvoke((Action)(() =>
  20:                  {
  21:                      bRange.Margin = GetThicknessByMaxMin();
  22:                      bRange.Height = (tempMinData - dataNotify.MinData) * THeight / (dataNotify.MaxRange - dataNotify.MinRange);
  23:                      txtMax.Text = tempMinData.ToString("0.0");
  24:                      txtMin.Text = dataNotify.MinData.ToString("0.0");
  25:                  }));
  26:                  if (tempMinData == dataNotify.MaxData)
  27:                      timer.Stop();
  28:              };
  29:              timer.Start();
  30:   
  31:              //当前值的显示
  32:              double tempMinRange = dataNotify.MinRange;
  33:              if (timerStep == null)
  34:                  timerStep = new DispatcherTimer();
  35:              timerStep.Interval = new TimeSpan(100);
  36:              timerStep.Tick += (sender, e) =>
  37:              {
  38:                  tempMinRange++;
  39:                  double value;
  40:                  if (dataNotify.MinRange < 0)
  41:                      value = (THeight / (dataNotify.MaxRange - dataNotify.MinRange)) * tempMinRange + (Math.Abs(dataNotify.MinRange)) * (120 / (dataNotify.MaxRange - dataNotify.MinRange));
  42:                  else
  43:                      value = (THeight / (dataNotify.MaxRange - dataNotify.MinRange)) * tempMinRange;
  44:                  if (value < 0)
  45:                      value = 0;
  46:                  bStep.Height = value;
  47:                  if (Math.Abs(tempMinRange - dataNotify.CurrentData) < 0.6)
  48:                  {
  49:                      timerStep.Stop();
  50:                  }
  51:              };
  52:              timerStep.Start();
  53:          }
  54:   
  55:          private readonly DispatcherTimer timer = null;
  56:          private readonly DispatcherTimer timerStep = null;
  57:          public DataNotify dataNotify;
  58:   
  59:          private const int THeight = 120;   //水银柱高度为120
  60:          private const int FHeight = 30;    //水银球高度为30
  61:   
  62:          //动态设置水银柱的Margin属性,以便于适应 带有正负值的场景
  63:          private Thickness GetThicknessByMaxMin()
  64:          {
  65:              double range = dataNotify.MaxRange - dataNotify.MinRange;
  66:              if (dataNotify.MinRange < 0)
  67:                  return new Thickness(0, 0, 0, FHeight + Math.Abs(dataNotify.MinRange)*THeight/range + (dataNotify.MinData) * THeight / range);
  68:              else
  69:                  return new Thickness(0, 0, 0, FHeight + (dataNotify.MinData) * THeight / range);
  70:          }
  71:   
  72:          private void UserControl_Loaded(object sender, RoutedEventArgs e)
  73:          {
  74:              //设置温度计的主题
  75:              switch (dataNotify.ThemeSet)
  76:              {
  77:                  case Theme.Red:
  78:                      dataNotify.BgImg = "Image/tem-red.png";
  79:                      dataNotify.BgStep = "Image/step-red.png";
  80:                      dataNotify.LineColor = "Red";
  81:                      break;
  82:                  case Theme.Blue:
  83:                      dataNotify.BgImg = "Image/tem-blue.png";
  84:                      dataNotify.BgStep = "Image/step-blue.png";
  85:                      dataNotify.LineColor = "Blue";
  86:                      break;
  87:   
  88:                  case Theme.Mo:
  89:                      dataNotify.BgImg = "Image/tem-mo.png";
  90:                      dataNotify.BgStep = "Image/step-mo.png";
  91:                      dataNotify.LineColor = "#00ACAE";
  92:                      break;
  93:   
  94:                  case Theme.Yellow:
  95:                      dataNotify.BgImg = "Image/tem-yellow.png";
  96:                      dataNotify.BgStep = "Image/step-yellow.png";
  97:                      dataNotify.LineColor = "#849C00";
  98:                      break;
  99:   
 100:                  case Theme.Orange:
 101:                      dataNotify.BgImg = "Image/tem-orange.png";
 102:                      dataNotify.BgStep = "Image/step-orange.png";
 103:                      dataNotify.LineColor = "#C88600";
 104:                      break;
 105:   
 106:                  case Theme.Green:
 107:                      dataNotify.BgImg = "Image/tem-green.png";
 108:                      dataNotify.BgStep = "Image/step-green.png";
 109:                      dataNotify.LineColor = "#178A00";
 110:                      break;
 111:   
 112:                  default:
 113:                      dataNotify.BgImg = "Image/tem-red.png";
 114:                      dataNotify.BgStep = "Image/step-red.png";
 115:                      dataNotify.LineColor = "Red";
 116:                      break;
 117:              }
 118:   
 119:              Draw(dataNotify.MinRange,dataNotify.MaxRange);
 120:          }
 121:   
 122:          //根据用户输入的最大刻度值,最小刻度值,来划刻度线
 123:          private void Draw(double minRange,double maxRange)
 124:          {
 125:              var step = (maxRange - minRange) / 120;
 126:              Line redLine = new Line();
 127:              redLine.X1 = 0;
 128:              redLine.Y1 = 0;
 129:              redLine.X2 = 0;
 130:              redLine.Y2 = 120;
 131:              redLine.StrokeThickness = 1;
 132:              redLine.Stroke = new SolidColorBrush(Colors.Black);
 133:              canvas1.Children.Add(redLine);
 134:              int j = 6;
 135:              for (int i = 0; i <= 6; i++)
 136:              {
 137:                  Line line = new Line();
 138:                  line.X1 = 0;
 139:                  line.Y1 = i * 20;
 140:   
 141:                  line.X2 =10;
 142:                  line.Y2 = i * 20;
 143:   
 144:                  TextBlock tb = new TextBlock();
 145:                  tb.Margin = new Thickness(12,i*20-8,0,0);
 146:                  tb.FontSize = 9;
 147:                  tb.Text = (((maxRange - minRange) / 6) * j + minRange).ToString("0");
 148:   
 149:                  line.StrokeThickness = 1;
 150:                  line.Stroke = new SolidColorBrush(Colors.Black);
 151:                  canvas1.Children.Add(line);
 152:                  canvas1.Children.Add(tb);
 153:                  for (int x = 0; x < 30; x++)
 154:                  {
 155:                      Line lineInner = new Line();
 156:                      lineInner.X1 = 0;
 157:                      lineInner.Y1 = (x + 1) * 4;
 158:   
 159:                      lineInner.X2 = 6;
 160:                      lineInner.Y2 = (x + 1) * 4;
 161:   
 162:                      lineInner.StrokeThickness = 1;
 163:                      lineInner.Stroke = new SolidColorBrush(Colors.Black);
 164:                      canvas1.Children.Add(lineInner);
 165:                  }
 166:   
 167:                  j--;
 168:              }
 169:          }
 170:   
 171:      }

上面我加了一部分注释,解释的比较清楚了。由于写这个温度计的时候,我们时刻需要测量好屏幕上的水银柱高度和当前值的对应关系,所以里面有比较多的运算,具体的运算方式还希望能够自己推敲。代码我将在下一篇中附上。

Silverlight自定义控件开发:温度计,布布扣,bubuko.com

时间: 2024-08-02 02:38:28

Silverlight自定义控件开发:温度计的相关文章

Silverlight自定义控件开发:仪表盘

在项目中,由于使用到了活动积温运算,也就是指当日平均气温稳定上升到10℃以上时,大多数农作物才能活跃生长.把大于等于10℃持续期内的日平均气温累加起来,得到的气温总和,叫做活动积温.所以我决定采用dojo的原生仪表盘的图片素材进行封装,作出一个silverlight版本来.下面是其界面截图和具体的调用方法: 调用方法如下: 1: Data d = new Data(); 2: d.Val = 40; 3:   4: var uc = new ChartControl(d); 5: Test.Ch

C#自定义控件开发

自定义控件开发 一般而言,Visual Studio 2005中自带的几十种控件已经足够我们使用了,但是,在一些特殊的需求中,可能需要一些特殊的控件来与用户进行交互,这时,就需要我们自己开发新的.满足用户需求的控件. 要开发自己的控件,有几种方法: ?复合控件(Composite Controls):将现有的各种控件组合起来,形成一个新的控件,来满足用户的需求. ?扩展控件(Extended Controls):就是在现有的控件基础上,派生出一个新的控件,增加新的功能,或者修改原有功能,来满足用

ASP.NET 2.0 自定义控件开发(一)页面呈现[转]

ASP.NET 2.0 自定义控件开发(一)页面呈现[转] http://www.cnblogs.com/yanyangtian/archive/2008/08/25/1275741.html  此网页文章不错. 编写自定义控件首先要选择基类. 1.所有的标准控件都可以作为基类.你可以选择你需要的标准控件来重写新的控件 2.如果从标准控件找不到适合的基类,则可以使用以下三种中的一种 a)System.Web.UI.Control(所有ASP.NET控件的基类) b)System.Web.UI.W

Android 自定义控件开发入门(一)

作为一个有创意的开发者,或者软件对UI设计的要求比较高,你经常会遇到安卓自带的控件无法满足你的需求的情况,这种时候,我们只能去自己去实现适合项目的控件.同时,安卓也允许你去继承已经存在的控件或者实现你自己的控件以便优化界面和创造更加丰富的用户体验. 那么怎样来创建一个新的控件呢? 这得看需求是怎样的了. 1.需要在原生控件的基本功能上进行扩展,这个时候你只需要继承并对控件进行扩展.通过重写它的事件,onDraw ,但是始终都保持都父类方法的调用.如从已有的高级控件上继承,例如继承一个TextVi

Android 自定义控件开发入门(二)

上一次我们讲了一堆实现自定义控件的理论基础,列举了View类一些可以重写的方法,我们对这些方法的重写是我们继承View类来派生自定义控件的关键 我通过一个最简单的例子给大家展示了这一个过程,无论是多么复杂的自定义控件,思路总是这样子的,但是因为我们仅仅重写了onDraw方法使得大家觉得怪怪的,作为一个控件,我们居然还要为了他的实现为其增加麻烦的监听,这就不能叫做控件了. 下面再给大家介绍一个经常重写的方法法:publicboolean onTouchEvent (MotionEvent even

Asp.net 自定义控件开发相关的几种嵌入资源解决方案

前提: 如下将要介绍的几种类型资源都要在其属性页窗口, 将 <生成操作> 属性, 设置为[嵌入的资源], 如图: ? 给自定义控件添加自定义图标的几种方案 方法一: 直接在自定义控件项目中添加一个 *.bmp格式的图标文件, 并将其命名 与主控件文件相同, 扩展名为 .bmp, 比如主控件文件名为: CustomButton.cs, 则图标文件命名为: CustomButton.bmp . 编译项目. 然后在工具箱中添加此控件就可以看到刚刚设置的图标效果. 方法二: 图标文件名称与主控件名称不

Android 自定义控件开发入门 (三)

上两次我们从如何自定义控件讲起,列举了View的一些Api,说明了一些在自定义的时候,可以进行重写的方法,然后通过一个例子的两种写法向大家展示了最基本的自定义控件和我们要充分了解并积极重写View方法的精神,这次我们将继续进行学习! 现在请大家回想一下我们使用安卓原生控件时的感受,一个好的控件是可以在xml中进行各种属性的操作的,而自定义控件往往有一些特殊的需求,今天我要讲的就是安卓给自定义控件添加自定义的属性. 下面再给大家具体介绍一下如果自定义的View需要有自定义的属性我们该如何处理: 我

iOS 自定义控件开发

工作需要,最近在进行iOS方面的图表工作.找了很多第三方库都无法实现效果,所以决定自己写一个控件. #0 目标 希望可以写一个通用的图表控件(仅针对此项目),虽然开发难度增大,但是可以学习到很多知识.并且控件使用简单,可以自适应大小,支持屏幕旋转. #1 准备工作 网上各种查资料 研究了一下系统自带控件,全部基于UIView 开发过程中使用storyboard,在页面中加入一个View来控制大小,自定义控件放入此view中并且填充满,让程序可以自适应屏幕尺寸. #2 开始自定义 创建自定义控件,

自定义控件开发流程及总结

原文地址http://blog.csdn.net/dinglin_87/article/details/7431545,原文有demo,但是原文没有代码格式,我把代码部分都重新放置了 本文只是简述自定义控件的步骤, 没有实现华丽的界面效果,仅供参考!自定控件步骤如下: 1.写一个类继承View或你想扩展功能的控件(比如TextView). public class CustomView extends View { }; 2.在/res/value下创建一个attr.xml文件.没有这个文件自定