【Android】自定义控件——仿天猫Indicator

今天来说说类似天猫的Banner中的小圆点是怎么做的(图中绿圈部分)



在学习自定义控件之前,我用的是非常二的方法,直接在布局中放入多个ImageView,然后代码中根据Pager切换来改变图片。这种方法虽然可以在切换完成后正确显示小圆点,但是却做不到如下图中的切换中的状态显示:




今天学到了自定义控件,正好可以把这个坑填上。



说一下思路:

在ViewPager翻动的时候,会调用

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);

其中positionOffset为翻动的百分比,所以只要将这个参数传入自定义控件,就可以计算出实心圆点的坐标


实战:

PagerIndicator.java

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class PagerIndicator extends View {
	// 空心圆半径
	private int RADIUS = 10;
	// 空心圆画笔
	private Paint mBgPaint;
	// 实心圆画笔(当前页)
	private Paint mPaint;
	// 圆点个数,默认为5,设计布局时可以预览
	private int mCount = 5;
	// 当前实心圆的位置
	private int mPosition;
	// 偏移量(百分比)
	private float mOffset;
	// 第一个空心圆的圆心坐标
	private int startY;
	private int startX;

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		// 画出空心的小圆点
		for (int i = 0; i < mCount; i++) {
			canvas.drawCircle(startX + i * 3 * RADIUS, startY, RADIUS, mBgPaint);
		}

		// 画出指示当前位置的原点,由于高度固定,所以只计算了X坐标
		float x = startX + (mPosition + mOffset) * 3 * RADIUS;
		canvas.drawCircle(x, startY, RADIUS - 1, mPaint);
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		// 获取第一个空心圆的圆心坐标
		startX = w - 3 * RADIUS * mCount;
		startY = h / 2;
		super.onSizeChanged(w, h, oldw, oldh);
	}

	// 设置圆个数
	public void setCount(int count) {
		mCount = count;
	}

	// 获取偏移量并重绘indicator
	public void onPageScrolled(int position, float offset) {
		mPosition = position;
		mOffset = offset;
		invalidate();
	}

	// 创建带AttributeSet参数的构造方法使控件可以直接拖动到布局中并预览
	public PagerIndicator(Context context, AttributeSet attrs) {
		super(context, attrs);
		initPaint();
	}

	// 初始化画笔
	private void initPaint() {
		// 空心圆画笔
		mBgPaint = new Paint();
		mBgPaint.setColor(Color.GRAY);
		mBgPaint.setAntiAlias(true);
		mBgPaint.setStyle(Paint.Style.STROKE);
		mBgPaint.setStrokeWidth(2);
		// 实心圆画笔
		mPaint = new Paint();
		mPaint.setColor(Color.rgb(197, 135, 87));
		mPaint.setAntiAlias(true);

	}

}

布局的设置:


XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp" >

    <android.support.v4.view.ViewPager
        android:id="@+id/banner_pager"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#C0FFFF" />


    <这里改成你自己的包名.PagerIndicator
        android:id="@+id/pagerIndicate1"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:background="#C0C0C0"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true" />

</RelativeLayout>

在使用到的地方:

初始化的方法,反正只要比使用早调用到就行

private void initPagerIndicator() {
	mPagerIndicator = (PagerIndicator) mLayout.findViewById(R.id.pagerIndicate1);
	//此处直接设置为7,有需求可自己更改成其他值
	mPagerIndicator.setCount(7);
}
设置ViewPager监听器:

mPager.setOnPageChangeListener(new OnPageChangeListener() {
			
	@Override
	public void onPageSelected(int arg0) {
	}
			
	@Override
	public void onPageScrolled(int position, float offset, int arg2) {
		//重点在这里,调用控件内的方法
		mPagerIndicator.onPageScrolled(position , offset);				
	}
			
	@Override
	public void onPageScrollStateChanged(int arg0) {
	}
});


完成的效果:


颜色选得比较随意,觉得难看自己换掉吧,毕竟我没有美工QAQ。。。


chenbw1

2014/5/8

欢迎转载,转载请注明出处,谢谢


最后吐槽下,原来优快云有私信啊。。。之前完全就没注意过。。。所以一些信息现在才看到也就不回了,毕竟都过去好久了,抱歉啦。。。。。。。。。。。


<think>我们正在开发一个用于工业自动化场景的指示灯自定义控件。指示灯控件通常有两种状态:开(亮)和关(灭),并且可能有不同的颜色表示不同的状态(例如,绿色表示正常,红色表示故障)。在WinForms中,我们可以通过创建自定义控件来实现这个功能。 步骤: 1. 创建一个新的Windows控件库项目。 2. 设计控件的属性和事件。 3. 重写控件的绘制逻辑。 4. 测试控件。 具体实现: 1. 创建项目: 在Visual Studio中,选择“Windows 窗体控件库”项目模板,命名为“IndicatorLight”。 2. 设计控件: 我们将创建一个继承自Control类的控件。我们将添加一些属性,如: - State(状态):表示指示灯的状态(开或关) - OnColor(开状态的颜色) - OffColor(关状态的颜色) - LightSize(指示灯的直径) 3. 重写OnPaint方法: 在OnPaint方法中,我们将绘制一个圆形来表示指示灯,并根据状态选择颜色。 4. 添加事件: 我们可以添加一个事件,当状态改变时触发。 5. 测试控件: 在另一个Windows窗体应用程序中引用这个控件库,然后将控件拖到窗体上进行测试。 代码实现: 首先,我们创建一个自定义控件类: ```csharp using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace IndicatorLight { [DefaultEvent("StateChanged")] public partial class IndicatorLight : Control { // 状态属性 private bool _state; [Description("指示灯的当前状态(开/关)"), Category("Indicator")] public bool State { get { return _state; } set { if (_state != value) { _state = value; this.Invalidate(); // 重绘控件 OnStateChanged(EventArgs.Empty); } } } // 开状态颜色 private Color _onColor = Color.LimeGreen; [Description("指示灯在开状态时的颜色"), Category("Indicator")] public Color OnColor { get { return _onColor; } set { _onColor = value; this.Invalidate(); } } // 关状态颜色 private Color _offColor = Color.Gray; [Description("指示灯在关状态时的颜色"), Category("Indicator")] public Color OffColor { get { return _offColor; } set { _offColor = value; this.Invalidate(); } } // 指示灯大小(直径) private int _lightSize = 20; [Description("指示灯的直径"), Category("Indicator")] public int LightSize { get { return _lightSize; } set { if (value < 10) value = 10; if (value > 100) value = 100; _lightSize = value; // 调整控件大小以适应指示灯 this.Size = new Size(_lightSize + 2, _lightSize + 2); this.Invalidate(); } } // 状态改变事件 [Description("当指示灯状态改变时触发"), Category("Indicator")] public event EventHandler StateChanged; public IndicatorLight() { InitializeComponent(); // 设置双缓冲以减少闪烁 this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true); this.Size = new Size(_lightSize + 2, _lightSize + 2); } protected virtual void OnStateChanged(EventArgs e) { StateChanged?.Invoke(this, e); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 计算圆形的位置,使其居中 int x = (this.Width - _lightSize) / 2; int y = (this.Height - _lightSize) / 2; // 绘制背景(圆形) Color lightColor = _state ? _onColor : _offColor; using (Brush brush = new SolidBrush(lightColor)) { g.FillEllipse(brush, x, y, _lightSize, _lightSize); } // 绘制边框 using (Pen pen = new Pen(Color.Black, 1)) { g.DrawEllipse(pen, x, y, _lightSize, _lightSize); } } // 点击控件切换状态 protected override void OnClick(EventArgs e) { base.OnClick(e); State = !State; } // 当控件大小改变时,调整到合适的大小(保持正方形) protected override void OnResize(EventArgs e) { base.OnResize(e); // 保持控件为正方形,以圆形显示 int size = Math.Min(this.Width, this.Height); if (size != this.Width || size != this.Height) { this.Size = new Size(size, size); } } } } ``` 注意:上面的代码中,我们通过点击控件来切换状态(仅用于测试,实际工业应用中可能不需要点击切换,而是通过程序设置State属性)。 6. 测试控件: 创建一个Windows窗体应用程序,添加对控件库项目的引用。然后,在工具箱中会看到IndicatorLight控件,将其拖到窗体上。 在窗体上,我们可以通过代码设置状态: 例如:indicatorLight1.State = true; 此外,我们还可以在属性窗口中设置OnColor和OffColor等属性。 优化: - 为了更好的视觉效果,我们可以添加一个光泽效果(高光)使指示灯看起来更立体。 - 支持不同的形状(如矩形、三角形)等。 但是,作为基础教程,我们只实现了圆形指示灯。 以上是一个简单的指示灯自定义控件的实现。 相关问题: 1. 如何在自定义控件中添加光泽效果(高光)? 2. 如何让指示灯支持不同的形状(如矩形、三角形)? 3. 如何实现指示灯的状态绑定到数据源? 4. 在工业自动化中,指示灯控件通常需要支持闪烁状态,如何实现闪烁功能?</think>### C# WinForms 自定义指示灯控件开发指南 在工业自动化场景中,指示灯控件用于直观显示设备状态(如运行/停止/故障)。以下是实现步骤: #### 1. 创建自定义控件项目 1. 新建「Windows 窗体控件库」项目 2. 重命名默认的 `UserControl1.cs` 为 `IndicatorLight.cs` #### 2. 核心属性设计 ```csharp public enum LightState { Off, On, Flashing } private LightState _state = LightState.Off; [Description("指示灯当前状态"), Category("Indicator")] public LightState State { get => _state; set { _state = value; this.Invalidate(); // 触发重绘 StartFlashingIfNeeded(); } } private Color _onColor = Color.LimeGreen; [Description("点亮时的颜色"), Category("Indicator")] public Color OnColor { get => _onColor; set { _onColor = value; Invalidate(); } } private Color _offColor = Color.Gray; [Description("熄灭时的颜色"), Category("Indicator")] public Color OffColor { get => _offColor; set { _offColor = value; Invalidate(); } } private int _flashInterval = 500; [Description("闪烁间隔(毫秒)"), Category("Indicator")] public int FlashInterval { get => _flashInterval; set { _flashInterval = value; if (_flashTimer != null) _flashTimer.Interval = value; } } ``` #### 3. 闪烁功能实现 ```csharp private Timer _flashTimer; private bool _isFlashingOn; private void StartFlashingIfNeeded() { if (State == LightState.Flashing) { if (_flashTimer == null) { _flashTimer = new Timer(); _flashTimer.Tick += (s, e) => { _isFlashingOn = !_isFlashingOn; Invalidate(); }; } _flashTimer.Interval = FlashInterval; _flashTimer.Start(); } else { _flashTimer?.Stop(); } } ``` #### 4. 绘制指示灯 ```csharp protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; // 计算圆形区域 int diameter = Math.Min(Width, Height) - 4; Rectangle rect = new Rectangle( (Width - diameter) / 2, (Height - diameter) / 2, diameter, diameter); // 选择颜色 Color lightColor = OffColor; if (State == LightState.On) lightColor = OnColor; if (State == LightState.Flashing && _isFlashingOn) lightColor = OnColor; // 绘制渐变效果 using (var brush = new LinearGradientBrush( rect, ControlPaint.Light(lightColor, 0.8f), ControlPaint.Dark(lightColor, 0.2f), 45f)) { g.FillEllipse(brush, rect); } // 绘制边框 using (var pen = new Pen(ControlPaint.Dark(BackColor, 0.5f), 1.5f)) { g.DrawEllipse(pen, rect); } } ``` #### 5. 控件优化 ```csharp // 在构造函数中添加 SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); // 确保释放资源 protected override void Dispose(bool disposing) { _flashTimer?.Dispose(); base.Dispose(disposing); } ``` #### 6. 使用示例 ```csharp // 在窗体中使用 private IndicatorLight indicator1; private void InitializeComponent() { indicator1 = new IndicatorLight(); indicator1.Size = new Size(40, 40); indicator1.Location = new Point(20, 20); indicator1.OnColor = Color.Red; // 设置报警色 Controls.Add(indicator1); } // 更新状态 void UpdateMachineStatus(bool isRunning) { indicator1.State = isRunning ? LightState.On : LightState.Off; } ``` #### 工业场景增强建议 1. **3D效果优化**:使用路径渐变创建立体感 2. **状态绑定**:实现 `INotifyPropertyChanged` 接口支持数据绑定 3. **安全特性**:添加线程安全的属性更新方法 4. **多语言支持**:状态描述本地化资源 5. **SVG导出**:实现设备状态图的导出功能 --- ### 相关问题 1. 如何在自定义控件中实现数据绑定以响应PLC信号变化? 2. 工业场景下指示灯控件需要哪些额外的安全特性? 3. 如何优化自定义控件的设计时体验(如工具箱图标、智能标签)? 4. 在WinForms中如何实现控件的高DPI支持以适应工业触摸屏? 5. 如何扩展指示灯控件支持多状态(如运行/警告/故障)显示? > 工业自动化控件开发需特别注意实时性和线程安全,建议使用 `Invoke` 方式更新UI状态[^1]。对于关键状态指示,应添加状态变更日志记录功能以满足工业审计要求[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值