以下是效果
1. 在 Matlab 里安装两个额外的库: hatchfill2 和 legendflex。
(1)搜索并安装 hatchfill2,用来画纹理
(2) 搜索并安装 legendflex,用来画自定义的图例
2. 代码(说明见注释)
data = rand(8, 5);% 图例文本,因为这个例子是 stacked bar chart, data 数组的每一行是一根柱子,每一列是其中的
% 一段,所以一共有 8 条柱子,每条都被分为了 5 段,每一段是一个 category.
legendData = {'Category 1', 'Category 2', 'Category 3', 'Category 4', 'Category 5'};% 颜色数组,可以设置为任何喜欢的颜色,hex2rgb 会把 16 进制的颜色数据转化为 rgb 数组
color_vec = [hex2rgb("#005C53");hex2rgb("#9FC131");hex2rgb("#DBF227");hex2rgb("#D6D58E");
];% 预定义的纹理组合,尽量让 texture_types 的数目和 color_vec 互质,这样轮询起来
% 可以得到的组合更多
texture_types = {struct('HatchStyle', 'single', 'HatchAngle', 45, 'HatchDensity', 20, 'HatchColor', 'black', 'HatchLineWidth', 0.5);struct('HatchStyle', 'single', 'HatchAngle', -45, 'HatchDensity', 20, 'HatchColor', 'black', 'HatchLineWidth', 0.5);struct('HatchStyle', 'single', 'HatchAngle', 90, 'HatchDensity', 20, 'HatchColor', 'black', 'HatchLineWidth', 0.5);struct('HatchStyle', 'single', 'HatchAngle', 0, 'HatchDensity', 20, 'HatchColor', 'w', 'HatchLineWidth', 0.5);struct('HatchStyle', 'cross', 'HatchAngle', 30, 'HatchDensity', 15, 'HatchColor', 'black', 'HatchLineWidth', 0.5);
};% 创建图形并调整大小
figure('Position', [100, 100, 800, 600]);
hold on;% 初始化柱状图对象单元数组
bars = cell(size(data, 1), 1);% 绘制堆叠的柱状图并设置底色和纹理填充
for i = 1:size(data, 1)% 如果把'stacked'去掉,就会得到分组的柱状图b = bar(i, data(i, :), 'stacked', 'FaceColor', 'flat');bars{i} = b; % 存储主 bar 对象for j = 1:size(data, 2)color_idx = mod(j - 1, size(color_vec, 1)) + 1;texture_idx = mod(j - 1, length(texture_types)) + 1;b(j).FaceColor = color_vec(color_idx, :);% 应用纹理组合tex = texture_types{texture_idx};hatchfill2(b(j), tex);end
end% 将 bars 转换为矩阵以用于 legendflex
barsMatrix = [bars{:}];% 调整坐标轴的位置以给图例腾出空间,如果不设置,图例肯能会画到外面,只显示
% 一部分
pos1 = get(gca, 'Position');
set(gca, 'Position', [pos1(1), pos1(2), pos1(3) * 0.75, pos1(4)]);% 创建自定义图例并设置位置
[legend_h, object_h, plot_h, text_str] = legendflex(barsMatrix, legendData, ...'FontSize', 14, 'ref', gca, 'anchor', [4 8], 'buffer', [10 0]);% 为图例中的柱状图添加纹理填充
numPatches = numel(object_h) - numel(legendData); % 计算图例中需要添加纹理的补丁数量
for k = 1:numPatchestexture_idx = mod(k - 1, length(texture_types)) + 1;tex = texture_types{texture_idx};hatchfill2(object_h(numel(legendData) + k), tex);
end% 设置坐标轴属性
set(gca, 'FontSize', 14);
grid on;
% set(gca, 'XMinorTick', 'on', 'XMinorGrid', 'on', 'YMinorTick', 'on', 'YMinorGrid', 'on');% 设置 x 轴和 y 轴范围
xlim([0, size(data, 1) + 1]);
ylim([0, max(sum(data, 2)) * 1.1]);% 设置 x 轴刻度标签
set(gca, 'XTick', 1:size(data, 1), 'XTickLabel', arrayfun(@(x) sprintf('Group %d', x), 1:size(data, 1), 'UniformOutput', false));% 显示图形
hold off;% hex2rgb 函数
function rgb = hex2rgb(hex)hex = char(hex);assert(size(hex, 2) == 7 && hex(1) == '#', 'Input must be a char array of the form "#RRGGBB"');rgb = reshape(sscanf(hex(2:end), '%2x') / 255, 1, 3);
end
3. 图例位置调整
参考 legendflex的文档,anchor 参数里设两个数字,表示图片的哪个锚点和图例的哪个锚点对齐,例如,上述例子中 [4 8], 4 表示图片的 east 锚点,8表示图例的 west 锚点。这一步让图例的左侧紧贴图片的右侧。buffer 参数定义图例的偏移量,[10 0]表示让图例向右偏移10个单位。
- 1 ('nw'): 西北角
- 2 ('n'): 北边中心
- 3 ('ne'): 东北角
- 4 ('e'): 东边中心
- 5 ('se'): 东南角
- 6 ('s'): 南边中心
- 7 ('sw'): 西南角
- 8 ('w'): 西边中心
如果图例显示不全,可以用 set(gca, 'Position', ...) 调整位置,给图例腾出空间。
参考
- Hatchfill2: https://www.mathworks.com/matlabcentral/fileexchange/53593-hatchfill2
- legendflex: https://www.mathworks.com/matlabcentral/fileexchange/31092-legendflex-m-a-more-flexible-customizable-legend
- Simeon 的回答:https://www.mathworks.com/matlabcentral/answers/478956-getting-hatchfill-to-properly-display-a-patch-legend#answer_920769